anna.dat0000444000175000017500000002320405407367705010623 0ustar dondon* File "anna.dat" from the Stanford GraphBase (C) 1993 Stanford University * Anna Karenina, by Leo Nikolaevitch Tolstoy * This file may be freely copied but please do not change it in any way! * (Checksum parameters 378,911441608) AA Annushka, maid of AN AG Agafea Mihalovna, housekeeper of LE AL Alexey Alexandrovitch Karenin, minister of state AN Anna Arkadyevna Karenina, wife of AL AO Aliosha, son of DO and ST AP Anna Pavlovna, wife of PV BA Annie, baby of AN and VR BD Dmitri (Mitya), baby of LE and KI BE Madame Berthe, blind woman BL Count Bol, friend of KI in Moscow BN Bartnyansky, rich man in Petersburg BO Countess Bola, wife of BL BT Princess Betsy Tverskaya, cousin of VR CA Count Anitchkin, supervisor of ST CB Countess Bonina, dance partner of YK CD Colonel Demin, colleague of VR CN Countess Nordston, friend of KI CO Cord, English horse trainer CV Countess Vronskaya, mother of VR DO Princess Darya Alexandrovna Oblonskaya (Dolly), wife of ST ED Miss Edwards, English governor of SE EF Marya Efimovna, nurse of AL FC Fyodor 1, coachman of AA FR Fyodor Ryezunov, carpenter FY Fyodor 2, peasant GA Gagin, officer from Petersburg GO Golenishtchev, friend of VR GR Grisha, young son of ST and DO GV Grinevitch (Mihail Stanislavitch), board member HA Hannah, pupil of AN HO Miss Hoole, English governess to DO's children IG Ignat, coachman of LE IV Ivan 1, cowherd IW Ivan 2, coachmen of LE JL Jules Landau (Count Bezzubov), psychic KA Captain Kamerovsky, cavalry officer KE Prince Kedrov, member of VR's regiment KI Princess Ekaterina Alexandrovna Shtcherbatskaya (Kitty), wife of LE KO Sergei Ivanovitch Koznishev, half-brother of LE KP Kapitonitch, hall porter of AL KR Kritsky, friend of NI KT Professor Katavasov, natural scientist KU Prince Kuzovlev, fearful horseman KV Krivin, bald socialite KY Korney, valet of AL KZ Kouzma, elderly servant of LE LE Konstantin Dmitrievitch Levin, proprietor of Pokrovskoe LI Countess Lidia Ivanovna, Petersburg dogooder LK Lidi Korsunskaya, wife of YK LL Lily, youngest child of DO and ST LM Liza Merkalova, thin brunette admired by SM LP Lizaveta Petrovna, midwife LV Arseny Lvov, husband of NA MA Matvey, valet of ST MB Princess Marya Borissovna, KI's godmother MC Mihail, coachman MD Marya Dmitrievna, aunt of KI ME Mariette, governess of SE MH Mahotin, rival horseman to VR MI Mihailov, painter MJ Mihailitch, beekeeper MK Mishka, peasant lad ML Mademoiselle Linon, French governess of KI MM Masha 3, little daughter of ST and DO MN Marya Nikolaevna, companion of NI MO Metrov, Petersburg social scientist MP Mihael Petrovitch, landowner MQ Masha 2, maid of KI MR Mademoiselle Roland, French governess MS Madame Stahl, invalid philanthropist MT Masha Tchibisova, dancer MV Marya Vlasyevna, midwife MX Masha 1, young relative of BT MY Princess Myakaya, enfant terrible MZ Madame Sviazhskaya, wife of SV NA Princess Natalia Lvova, sister of DO and KI ND Nadinka, niece of LI NI Nikolay Levin, brother of LE NL Nikolinka, son of DO and ST NN Nikitin (Philip Ivanovitch), board member NS Nikolay Shtcherbatsky, cousin of KI NT Nastia, sister of MV NV Madame Nikolaevna, KI's maid of honor NY Nevyedovsky, malignant gentleman PA Parmenitch, old beekeeper PC Prince Tchetchensky, man with two families PD Pyotr Dmitrievitch, doctor PE Pestsov, eccentric enthusiast PH Matrona Marya Philimonovna, nurse PK Prince Kaluzhsky, Petersburg party guest PO Princess Oblonskaya, unmarried aunt of AN PP Prince Pyotr Oblonsky, man of sixty PR Prince Alexander Shtcherbatsky, father of DO and KI PS Princess Shtcherbatskaya, mother of DO and KI PT Lieutenant Petritsky, friend of VR PV Mihail Alexeyevitch Petrov, painter PX Pyotr, servant of AN PY Prohor Yermilin, mower RT Marya Yevgenyevna Rtishtcheva, lady of Moscow RY Mihail Ignatitch Ryabinin, merchant SA Sasha, wife of MI SE Sergey Alexeyevitch Karenin (Seryozha), son of AL and AN SH Baroness Shilton, friend of PT SI Vassily Lukitch Sitnikov, tutor of SE SL Mihail Vassilievitch Sludin, secretary of AL's department SM Stremov, opponent of AL SN Stepan Vassilievitch, landowner SO Princess Sorokina, young friend of CV SP General Serpuhovskoy, rival of VR SQ Snetkow, marshal of Kashinsky province SS Sappho Shtolz, blonde beauty ST Prince Stepan Arkadyevitch Oblonsky (Stiva), brother of AN SU Shuraev, peasant SV Nikolay Ivanovitch Sviazhsky, landowner SY Semyon, contractor to LE TA Tanya, oldest daughter of ST and DO TB Madame Trubetskaya, wedding guest TC Tchirikov, best man of LE TT Tit, mower TU Tushkevitch, croquet player TV Turovtsin, party guest VA Varya, wife of XV VE Venden, mustachioed clerk VF Vassily Fedorovitch, bailiff of LE VG Volgarinoff, Jew VI Pyotr Ilyitch Vinovsky, club friend of ST VK Varvara Andreevna (Varenka), adopted daughter of MS VO Ivan Petrovich Vorkuev, publisher VP Princess Varvara, aunt of ST VR Count Alexey Kirillovitch Vronsky, young officer VS Vaska, admirer of SS VV Vassenka Veslovsky, distant cousin of KI VY Voytov, horse purchaser XV Alexander Vronsky, brother of VR YG Yegor, servant in hotel YK Yegorushka Korsunsky, handsome dancer YS Young Shtcherbatsky, brother of KI YV Captain Yashvin, gambling friend of VR 1.1:ST,DO 1.2:ST,MR;ST,PH,MA;MA,DO 1.3:ST,GR,TA;ST,MA 1.4:DO,ST;ST,MA;DO,PH 1.5:ST,NN,GV,LE 1.6:NA,DO,KE,LE;NA,LV 1.7:KO,LE 1.8:KO,LE 1.9:LE,NS,KI;LE,PS,ST,KI;LE,ML,KI 1.10:LE,ST 1.11:LE,ST 1.12:PR,PS;PS,KI,VR 1.13:LE,KI 1.14:LE,KI,PS,CN,VR,PR 1.15:KI,PS,PR 1.16:VR 1.17:VR,ST 1.18:VR,ST,AN,CV 1.19:AN,DO,GR,TA 1.20:AN,DO,ST,KI;DO,KI,GR,TA 1.21:AN,DO,ST,KI;AN,VR 1.22:KI,PS,YK;CB,YK;LI,KV,ST,AN,VR;KI,YK,AN,VR 1.23:KI,VR,PS,CN;KI,VR,AN;CN,YK;YK,KI;YK,AN 1.24:LE,NI,MN,KR 1.25:LE,NI,MN,KR 1.26:LE,IG;LE,AG,KZ;LE,VF,SY 1.27:LE,AG 1.28:AN,DO,ST 1.29:AN,ST;AN,AA 1.30:VR,AN;AN,AL 1.31:VR,AN,AL 1.32:SE,AN;LI,AN 1.33:AL,AN;AN,SE 1.34:PT,SH,KA,VR 2.1:KI,PS;PS,PR 2.2:DO,PS,PR,KI 2.3:DO,KI 2.4:BT,AN,VR 2.5:VR,BT;PT,KE,VE,VR 2.6:BT,MY,VR 2.7:BT,MY,VR,AN,AL;AN,LI 2.8:AL 2.9:AN,AL 2.10:AN,AL,BT,VR 2.11:VR,AN 2.12:LE,NI;LE,AG 2.13:LE,VF;LE,IG 2.14:ST,LE,AG,KZ 2.15:ST,LE 2.16:ST,LE,RY 2.17:ST,LE,AG 2.18:VR 2.19:VR,YV 2.20:VR,PT,YV 2.21:VR,CO 2.22:VR,AN 2.23:VR,AN 2.24:VR,CO,MH;VR,XV;VR,ST;VR,KU 2.25:VR,KU,MH;VR,YV 2.26:AL,LI;AL,SL 2.27:AN,AA;AN,AL,SL;AN,AL,SE 2.28:AN,BT,AL;BT,ST 2.29:AN,BT,AL 2.30:PR,PS,KI,RT;KI,MS,VK;KI,NI,MN 2.31:PS,KI,NI;NI,VK,MN;PS,KI,VK 2.32:MS,VK;VK,PS,KI,RT 2.33:KI,MS,VK;PS,KI;KI,PV,AP 2.34:PR,PS,KI;PR,KI,BE;PR,KI,VK,MS;PR,KI,PV,AP 2.35:PR,KI,RT,VK,PS 3.1:KO,LE 3.2:KO,LE,AG 3.3:KO,LE;LE,VF 3.4:KO,LE;LE,VF;LE,TT 3.5:LE,MK,PY 3.6:LE,KO,KZ 3.7:ST,DO;DO,PH 3.8:DO,PH,TA,HO;DO,AO,LL,TA;DO,HO,GR,TA,NL 3.9:DO,LE,PH,LL 3.10:DO,LE,TA;DO,TA,GR 3.11:LE,PA 3.12:LE,DO 3.13:AL,AN 3.14:AL 3.15:AN,VR;AN,AA;AN,SE,ME 3.16:AN,AA 3.17:AN,BT,TU,MX 3.18:AN,BT,SS,VS,PK,SM,LM;LM,AN,SM,TU 3.19:VR,PT 3.20:VR 3.21:VR,PT,CD,SP,YV 3.22:VR,AN 3.23:AL,SM;AL,AN 3.24:VF,LE 3.25:LE 3.26:LE,SV,MZ,NT 3.27:LE,SV,MP,SN 3.28:LE,SV 3.29:LE,VF;LE,IV;LE,FR;LE,SU 3.30:LE,VF;LE,AG 3.31:LE,NI,AG 3.32:LE,NI;LE,NS 4.1:AL,AN;AN,VR 4.2:AL,VR;VR,AN 4.3:VR,AN;AN,LM 4.4:AL,AN 4.5:AL 4.6:AL,SM;BT,MY;AL,ST,DO 4.7:ST,MT;ST,LE;ST,CA 4.8:AL,ST 4.9:ST,KO,PE,PR,YS,TV,KI,AL,DO,LE,MA 4.10:PE,KO,AL,ST,TV,PR,DO 4.11:LE,KI,DO,TV 4.12:PE,ST,AL,KO,TV;AL,DO,YS 4.13:LE,PE,KO;LE,KI,YS;LE,KI,PR 4.14:LE,ST;LE,DO;KE,KO,SV;LE,YG 4.15:ML,LE;LE,KI,PR,PS 4.16:PS,PR,KI,LE;KI,LE,ML;LE,KO;LE,SV;KI,CN 4.17:AL,KP;AL,VR,AN;AN,BA 4.18:VR,KP;VR,VA 4.19:AL,BA;AL,SE,ED;AL,AN,BT 4.20:AL,AN 4.21:BT,ST;ST,AN 4.22:ST,AL 4.23:VR,VA;VR,BT;VR,AN 5.1:PS,LE;LE,KO;LE,ST;LE,KI 5.2:LE,KO,KT,TC;LE,KI,PS;KE,KO,SO,ST;LE,KZ 5.3:KI,NA;LE,ST,KZ 5.4:ST,DO,TC,LE,KI;LE,KI,CN;LE,KI,CA;KI,MD 5.5:DO,NA;LK,YK;LK,TB;PR,NV;KO,DO;ST,NA;NA,CN;CN,DO 5.6:LE,KI,PR,TC 5.7:VR,AN,GO 5.8:VR,AN 5.9:VR,AN,GO 5.10:MI,SA;MI,VR,GO,AN 5.11:MI,VR,GO,AN 5.12:MI,VR,GO,AN 5.13:MI,VR,AN,GO 5.14:LE,KI,AG,MQ 5.15:KE,KI,KZ 5.16:LE,KI,AG 5.17:NI,LE,KI;LE,MN,KI 5.18:NI,LE,KI,MN 5.19:LE,KI 5.20:NI,LE,KI,MN 5.21:AL,BT,ST;AL,ED;AL,KY;AL,SL 5.22:LI,AL,SE;AL,KY 5.23:LI 5.24:AL,LI 5.25:AL,LI 5.26:SE,KP;SE,SI;SE,ND 5.27:SE,SI;SE,AL 5.28:VR,CV,VA;VR,XV,AN;VR,BT,AN 5.29:AN,KP;AN,SE 5.30:KY,KP;KY,EF;EF,AN,SE,SI,AL 5.31:AN,BA;AN,VR,YV 5.32:AN,PO,VR,TU;AN,YV,VR,TU 5.33:VR,YV;VR,SP;AN,VV,YV;VA,VR,CV,SO;XV,VR;AN,VR,SM 6.1:DO,VK,KO,KI;KO,LE,KI,TV,VK;KI,PS 6.2:AG,PS,KI,DO,LE 6.3:KI,LE,PS 6.4:VK,KO 6.5:VK,GR,MM,KO,KI,LE 6.6:VK,KO,KI,LE,PS,DO,AG;LE,GR,ST,VV;ST,TA;ST,DO;PS,VV;VV,VK;KO,ST 6.7:KI,AG;LE,VV,ST,DO,KI;LE,PS,VK 6.8:VV,ST,LE;LE,KI 6.9:VV,ST,LE 6.10:VV,ST,LE 6.11:VV,ST,LE 6.12:LE 6.13:KI,MV;LE,ST,VV 6.14:LE,VV,ST,KI,PS,MV,DO;HO,TA 6.15:GR,MM;MM,DO,LE;LE,VV;LE,ST;DO,VK 6.16:DO,LE 6.17:VR,VV,AN,VP,SV,DO 6.18:DO,AN,VV,VR 6.19:DO,AA,AN;DO,AN,BA 6.20:DO,AN,VP,VV,VR,SV,TU 6.21:VR,DO 6.22:AN,DO,VP,VR,SV,VV,TU 6.23:AN,DO 6.24:DO,AN;AN,AL 6.25:AN,VR 6.26:LE,KO;LE,KI 6.27:LE,KO,SV,ST 6.28:LE,KO;KE,SQ 6.29:KO,ST,SV,VR;SV,LE,SN 6.30:SV,LE,VR,ST,KO,NY 6.31:NY,VR,SV,ST;AN,BA,VP 6.32:AN,BA;AN,VP,VR 7.1:LE,KI;KI,PR,MB,VR 7.2:KE,KI;LE,KZ 7.3:LE,KT,MO 7.4:LE,LV,NA 7.5:LE,NA;PE,LE;LE,BL 7.6:LE,BO;LE,SV;LE,ST;LE,NA;LE,KI 7.7:LE,PR;YV,VR,LE,ST,TV,GA,VI 7.8:LE,ST,VR,GA;LE,PR;VR,YV 7.9:LE,ST;AN,VO;LE,AN 7.10:LE,AN,VO,ST,HA 7.11:LE,ST;LE,KZ;LE,KI 7.12:AN,VR 7.13:LE,KI;LE,KZ;LE,LP 7.14:LE,LP,PS,KI,PD,DO 7.15:LE,PD,DO,LP,KI,PS,BD 7.16:PR,KO,ST,LE;LE,KI,PS,BD 7.17:ST,AL;ST,VG 7.18:ST,AL 7.19:ST,AL,SE;ST,AL,KY 7.20:ST,PC;ST,BN;ST,PP;ST,BT,MY 7.21:ST,BN;ST,LI,AL,JL 7.22:ST,PP;ST,LI,AL,JL 7.23:AN,VR,HA 7.24:AN,VR 7.25:AN,VR,AA;AN,VR,YV,VY 7.26:AN,VR;VR,SO 7.27:AN,BA;AN,AA;AN,MC;AN,PX 7.28:AN,DO,KI 7.29:AN,PX,AA 7.30:AN,PX,FC 7.31:AN,PX;AN,MC 8.1:KO 8.2:KO,KT,ST;ST,VR,CV 8.3:KO,KT 8.4:KO,CV 8.5:KO,VR 8.6:KO,KV,KI;KI,DO,PR;KI,AG,BD 8.7:AG,KI,BD;PR,KT 8.8:LE 8.9:LE,KO 8.10:LE 8.11:LE,FY 8.12:LE 8.13:LE,DO 8.14:LE,IW;GR,TA,KV,KO,DO,PR 8.15:DO,LE,KT,KO,PR,MJ,GR 8.16:LE,KT,KO,PR 8.17:DO,LE,KV,GR,TA;KO,PR;LE,AG;LE,KI,BD 8.18:LE,KT,KO,KI;LE,KI,BD 8.19:LE,KI * End of file "anna.dat" david.dat0000444000175000017500000001507505407367723011004 0ustar dondon* File "david.dat" from the Stanford GraphBase (C) 1993 Stanford University * David Copperfield, by Charles Dickens * This file may be freely copied but please do not change it in any way! * (Checksum parameters 152,151276216) AD Adams, head boy in DS's school AS Annie Strong, beautiful young wife of DS AW Agnes Wickfield, daughter of WW BA Mr. Barkis, willin' carrier BC Beauty Crewler, sister of ST BM Baby Murdstone, baby boy of CC and ED BT Betsey Trotwood, DC's paternal great-aunt CC Clara Copperfield, mother of DC CD Mr. Chillip, doctor at DC's birth CH Captain Hopkins, debtor with sonorous voice CK Mr. Creakle, proprietor of Salem House school CL Clicket (the Orfling), servant of WM CM Jack Maldon, ne'er-do-well cousin of AS CP Clara Peggotty, nurse to DC CR Mrs. Crupp, landlady to DC CS Miss Clarissa Spenlow, aunt of DO DB Richard Babley (Mr. Dick), weak-minded prot\'eg\'e of BT DC David Copperfield, our hero DL Mr. Dolloby, buyer of used clothing DM Miss Emma Micawber, daughter of WM and EM DO Dora Spenlow, daughter of FS DP Dan Peggotty, brother of CP DS Dr. Strong, schoolmaster at Dover DW Miss Mowcher, dwarf hairdresser ED Edward Murdstone, second husband of CC EM Mrs. Emma Micawber, sanguine wife of WM FG Mr. Grainger, friend of JS FM Mr. Markham, friend of JS FS Mr. Francis Spenlow, attorney GP Mr. Gulpidge, something to do with the law business GR Gregory, foreman of wine packers at Murdstone and Grinby GU Mrs. Gulpidge, dinner guest of FS HA Mrs. Henry Spiker, `Hamlet's Aunt' HC Rev. Horace Crewler, father of ST HP Ham Peggotty, nephew to CP and DP HS Mr. Henry Spiker, solicitor JK Mr. Jorkins, `hard-hearted' partner of FS JM Jane Murdstone, sister of ED JR Joram, partner of OM JS James Steerforth, schoolmate of DC JT Janet, maid to BT JU Julia Mills, bosom friend of DO KI Miss Kitt, creature in pink LA Miss Larkins, DC's older woman crush LC Mr. Chestle, `elderly' gentleman who marries LA LE Emily Peggotty (Little Em'ly), niece of DP LF Mr. Larkins, father of LA LM Littimer, valet to JS LS Miss Lavinia Spenlow, aunt of DO MC Mrs. Creakle, wife of CK ME Martha Endell, `fallen woman' MF Mrs. Fibbotson, old housemate of MM MG Mrs. Gummidge (Old Mawther), widow of DP's partner MH Mrs. Crewler, wife of HC MJ Master Wilkins Micawber Jr., son of WM and EM ML Charley Mell, teacher at Salem House MM Mrs. Mell, mother of ML MO Minnie Omer, daughter and seamstress to OM MP Mealy Potatoes, laborer at Murdstone and Grinby MS Mrs. Steerforth, mother of JS MW Mick Walker, laborer at Murdstone and Grinby OC Miss Creakle, daughter of CK OM Mr. Omer, haberdasher and funeral furnisher OS Mrs. Markleham (The Old Soldier), mother of AS PA Mary Anne Paragon, first servant of DC and DO PN Mr. Passnidge, friend of ED QU Mr. Quinion, manager of Murdstone and Grinby RD Rosa Dartle, companion to MS RW Red Whisker, rival for DO SA Miss Shepherd, adorable little girl SC Sarah Crewler, crippled sister of ST SH Mr. Sharp, headmaster at Salem House ST Sophy Crewler, fianc\'ee of TT TI Mr. Tiffey, clerk to FS TP Tipp, carman at Murdstone and Grinby TR Mr. Trotwood, husband left by BT TT Tommy Traddles, student at Salem House TU Mr. Tungay, one-legged guard at Salem House TW Micawber Twins, children of WM and EM UH Uriah Heep, articled to lawyer WW UM Mrs. Heep, 'umble mother of UH WA Mrs. Waterbrook, wife of WB WB Mr. Waterbrook, agent for WW WI William 1, friendly and hungry waiter WL William 2, coachman WM Wilkins Micawber, debtor who waits WW Mr. Wickfield, attorney at Dover 1:CC,BT;BT,CP;HP,CP;BT,CD;HP,BT;CC,DC 2:CC,DC,CP;DC,CP;DC,CP,ED;CP,CC;DC,ED;DC,ED,QU,PN;BA,DC,CP 3:CP,DC;CP,HP,DC;CP,HP,DC,LE,MG,DP;LE,DC;MG,CP;DC,CC,ED;BA,DC,CP 4:CC,CP,DC,ED;JM,DC,CC,ED;CC,ED,JM;DC,CC,JM,ED;DC,ED;CP,DC;JM,DC;BA,DC 5:BA,DC,CP;BA,DC;DC,WI;DC,ML,MM,MF;TU,ML,DC 6:DC,ML,TU;DC,TU,CK,MC,OC;TT,DC;JS,DC 7:TU,CK;CK,DC;TT,JS,DC;DC,JS;MM,JS,TT,DC,CK,TU;TU,DC;DP,HP,DC,JS 8:BA,DC;DC,CC,BM,CP;DC,CC,CP;DC,ED,JM;DC,JM,CC,BM;DC,ED,JM,CC;DC,CC,BM 9:DC,SH;CK,MC,DC;OM,MO,DC,JR;DC,CP,CC,BM;ED,CD,DC,JM;MG,ED,CD,DC,JR,MO 10:JM,CP,DC;DC,HP,DP,CP,BA;BA,CP,DP,MG,LE,DC,HP;DP,HP,LE,DC,MG;QU,DC,ED,JM 11:DC,MW,MP;QU,DC,WM;DC,WM,TW,MJ,DM,EM,CL;GR,TP,DC;EM,DC;CH,DC;DC,CP,WM 12:DC,EM,WM;WM,QU,TP,DC;DC,WM,EM,MJ,DM,CL;CP,DC;DC,MP 13:DC,DL;DC,JT,BT,DB 14:BT,JT,JM,ED,DC;BT,DC,JM,ED;BT,DC,JM,ED,DB;DB,DC,BT 15:DC,DB,BT,JT;DC,BT,UH;DC,BT,WW;DC,BT,WW,AW 16:WW,DC,DS,AS;AD,DC;CM,WW,DC,AW,UH;JM,DC,AS,DS,OS,WW,AW 17:DC,CP;BT,DB,TR;DS,DC,DB;UH,DC,UM,WM;DC,WM,EM 18:DC,SA;DC,AW;AD,DC;DC,LA;DC,LF;LC,DC 19:DC,BT,DB;DC,AW;DC,AW,DS,AS,OS,WW;WL,DC;JS,DC 20:DC,JS;MS,JS,DC,RD;MS,DC 21:DC,MS,JS,RD,LM;DC,MO,OM;DC,CP,BA;DC,CP,JS;DC,JS,HP,LE,DP,MG 22:DC,JS,MG;DC,JS,HP,LE;ME,LE,HP;DC,JS,LM;DC,JS,DW;DC,HP,LE,CP,ME 23:DC,JS,LM;DC,BT,JT;BT,TR;DC,BT,FS;DC,FS;DC,BT,CR;DC,BT,TI 24:DC,MS,RD;DC,JS;DC,CR;DC,JS,FG,FM;DC,AW 25:DC,AW;DC,AW,WA;DC,WB,WA,HS,HA,UH,AW,TT,GP,GU;DC,UH 26:DC,AW,UH;DC,FS;DC,DO,JM;DC,CR;DC,TI 27:DC,TT;DC,TT,WM;DC,TT,WM,EM;DC,WM 28:DC,CR;DC,TT,WM,EM;DC,TT,WM,EM,LM;DC,JS 29:DC,FS;DC,RD,MS,JS;DC,RD;DC,RD,JS;DC,JS,MS;DC,JS 30:DC,OM;DC,OM,MO,JR;DC,DP,CP,LE,HP;DC,CP,DP,BA 31:DC,CP;DC,CP,MG,DP;DC,CP,MG,DP,HP;DC,HP;LE,LM,JS 32:DC,HP,DP,MG;DC,MO;DC,DW;DC,HP,DP,MG,CP;DC,DP,RD,MS 33:DC,CP,FS,ED;JM,ED;DC,DO,JU,FS,RW,KI;TI,DC,CP 34:DC,AW;CR,DC;CP,TT;DC,TT;DC,CP,BT,DB,CR;DC,CP,BT,DB 35:DC,DB,BT,CP;DC,FS;DC,JK;DC,TI;WW,UH,AW,UM;DC,AW,BT,WW,UH 36:DC,DS;DC,DS,AS,CM;DC,DB,TT;DC,TT,DM,MJ,EM,WM 37:BT,CR;BT,CP,DC;DC,DO;DC,DO,JU;DC,JU 38:DC,TT,DB,BT;DC,FS,JM;DC,JU;DC,TI;DC,TI,JK;JU,DO;DO,LS,CS 39:DC,BT;DC,JT;DC,WM;DC,AW,WW,UH,UM 40:DC,BT,DB;DC,BT;DC,ME;DC,DP;DC,DP,ME;LE,MG 41:DC,LS,CS;DC,BT,DB,TT;TT,HC,MH,BC,SC,ST;TT,DC,LS,CS;DC,DO;BT,LS,CS,DO 42:DC,AW,LS,CS,DO;DC,WW,UH,DS;DC,DS,OS,AS;DB,DS;DB,AS;DC,BT;WM,EM 43:DC,TT;DC,DO,BT,LS,CS;DC,AW,DO,BT,CP,LS,CS,TT,ST;DC,DO,LS;DC,DO 44:DC,DO;DC,DO,PA;DC,BT;DC,DO,TT 45:DC,DS,OS;DS,OS,AS;DC,BT,AS,OS;DC,BT,OS,DS,AS,DB;DC,BT,DB 46:DC,RD;DC,RD,LM;LM,LE;DC,RD,MS;DC,DP;DC,DP,ME 47:DC,DP,ME;BT,TR;DC,BT 48:DC,DO;DC,DO,TT;DC,DO,BT 49:DC,WM;DC,TT;TT,EM;DC,TT,WM;DC,TT,WM,BT,DB 50:DC,DP;DP,ME;DC,ME;RD,LE,DC;DP,LE,DC 51:DC,DP,BT;DC,DO;DC,OM;DC,DP,CP,MG;DC,HP;DC,DP,MG 52:DC,BT,DO;DC,TT,BT,DB,WM,UH;DC,TT,BT,DB,WM,UH,AW,UM;DC,BT,WM,EM,DM,TW,MJ 53:DC,DO,BT;DC,DO;DC,BT,DO,AW;DC,AW 54:DC,AW,BT,WM,EM;DC,TT,BT,AW;DC,BT;DC,BT,TR;DC,BT,WM 55:DC,CP,DP;DC,BT;DC,LE;DC,HP,JS 56:DC,RD,MS 57:DC,WM,EM,TW,MJ,DM,TT,AW,BT,CP,DP;DC,CP,DP;DP,ME,DC;DC,CP,MG;LE,AW;DC,LE 58:DC,AW 59:DC,TT,ST;DC,TT;DC,CD;DC,BT,CP,DB;ED,JM 60:DC,BT;DC,AW;DC,AW,WW 61:DC,TT,ST;DC,TT;DC,CK,TT;DC,CK,TT,UH,LM;LM,DW 62:DC,BT;DC,AW;DC,BT,AW,DB,CP;DC,AW,TT,ST,DS,AS 63:DC,AW,DP;DP,LE,ME,MG,WM,EM,MJ,DM,ML;DC,AW,DP,BT,CP 64:DC,AW,BT,CP,DB;RD,MS;JU,CM;DS,AS,OS;DC,TT,ST;TT,HC,MC,BC;DC,AW * End of file "david.dat" econ.dat0000444000175000017500000005236605407367733010646 0ustar dondon* File "econ.dat" from the Stanford GraphBase (C) 1993 Stanford University * Input/Output structure of the US economy, 1985 * This file may be freely copied but please do not change it in any way! * (Checksum parameters 886,114400212) Industry: Goods: Natural resources: Organic resources: Living resources: Agriculture: Livestock and livestock products:1 Agriculture, excluding livestock:2 Forestry and fishery products:3 Fossil fuels: Coal mining:7 Petroleum and natural gas production:8 Inorganic resources: Mining of metals: Mining of ferrous metals:5 Mining of nonferrous metals:6 Mining of minerals: Quarrying of stone and clay:9 Mining of chemicals:10 Manufacturing: Organic products: Products from agriculture: Food and tobacco products: Food, liquor, and candy:14 Cigarettes, cigars, tobacco:15 Inedible products from agriculture: Textiles and leather: Textile manufacturing: Yard goods: Spinning and weaving:16 Specialized textile products:17 Fabricated textiles: Apparel:18 Household textiles:19 Leather manufacturing: Leather tanning and finishing:33 Leather products:34 Wood and paper: Wood manufacturing: Lumber and basic wood products: Lumber and wood, except containers:20 Wood containers:21 Furniture: Household furniture:22 Office furniture and fixtures:23 Paper manufacturing: Paper products: Paper products, except containers:24 Paperboard containers and boxes:25 Printing and publishing:26 Organic chemical products: Rubber and plastics: Rubber products:32 Plastics and synthetic materials:28 Petrochemicals: Petroleum refining and byproducts:31 Paints and allied products:30 Inorganic products: General inorganic products: Metal products: Primary metal manufacturing: Primary iron and steel manufacturing:37 Primary nonferrous metals manufacturing:38 Metal equipment: Tools and parts: Screw machine products and stampings:41 Metal products, not screws or stampings:42 Metal fixtures: Architectural metalwork:40 Metal containers:39 Mineral and chemical products: Inorganic chemical products: Drugs and toiletries:29 Fertilizers, glues, explosives, etc.:27 Mineral products: Stone and clay products:36 Glass and glass products:35 Specialized manufactured goods: Specialized static equipment: Precision instruments: Scientific and controlling instruments:62 Optical and photographic equipment:63 Sundries (jewelry, games, etc.):64 Specialized dynamic equipment: Vehicles and ordnance: Vehicles: Aircraft and parts:60 Land and water vehicles: Motor vehicles and equipment:59 Ships, trains, cycles, motor homes:61 Ordnance and accessories:13 Machinery and equipment: Electrical machinery and parts: Electrical parts: Electrical components: Electronic components and accessories:57 Batteries and other electric supplies:58 Electric lighting and wiring equipment:55 Electrical equipment: Electrical apparatus: Radio, TV, and communication equipment:56 Electrical appliances: Household appliances:54 Office, computing, and accounting machines:51 Industrial machinery: Electrical machines for service industries:52 Electrical apparatus for manufacturing:53 Non-electrical machinery and parts: Machine components: General industrial machinery equipment:49 Engines and components: Engine parts:50 Engines and turbines:43 Non-electrical machinery: Outdoor machinery: Farm and garden machinery:44 Construction and mining machinery:45 Machinery for manufacturing: Materials handling machinery and equipment:46 Industrial robots: Metalworking machinery and equipment:47 Special industry machinery and equipment:48 Services: Indirect services: Infrastructure: Construction: New construction:11 Repair and maintenance construction:12 Facilities: Transportation and communication: Transportation and warehousing:65 Communications: Communications, except radio and TV:66 Radio and television broadcasting:67 Utilities: Private utilities:68 Public utilities: Federal government enterprises:78 Local government enterprises:79 Economic services: Wholesale and retail trade:69 Financial services: Banking and insurance:70 Real estate and rental:71 Direct services: Commercial services: Business support services:73 Agricultural support services:4 Personal services: Health, education, and social services:77 Non-institutional personal services: Personal support services: Hotels, beauty care, etc.:72 Automobile repair and services:75 Recreational services: Eating and drinking places:74 Amusements:76 8490,2182,42,467,,,,,, ,,,59639,,84,58,3,4,7 ,,1,15,3,4,79,7,107, ,23,,,,,,,5,2 7,1,,,,,,3,, ,2,,1,,,,,, ,,,5,2,,,1,, ,3,19,545,,73,120,5,, 16775,2871,66,460,,,,3,, ,,,22546,3354,774,18,18,29,7 ,,1,9,1,2,143,54,119,2 2,10,,,,3,,,6,1 3,1,,,,,,2,, ,1,,,,,,,, ,1,,25,7,,,392,88,2 28,39,42,1202,,1114,187,1731,1, ,,281,26,,,,,, ,,,1465,,1,1,343,2,4601 5,3,4,44,1,,47,3,21,29 1,8,,,1,1,,,,10 ,7,,,,1,1,1,, ,,,,,1,,,2, 1,2,,7,,,,5,5, ,,,1112,,,23,22,, 3106,4127,1232,304,,1,11,1,1,2 1025,1972,1,223,,2,,1,,110 ,1,,4,1,1,235,13,5,1 5,1,,,1,3,1,1,,1 ,2,1,,,,,,, 1,,2,,1,3,2,,2,2 1,,2,1,14,2,,36,1631,9 3140,111,23,3,,169,258,2,8, ,,,,184,1,,,, ,,2,1,,,,,,1 ,,,1,1,1,135,6,2,9 2,,,,,6,2452,17,,7 6,65,,1,1,1,5,1,3,1 1,,1,,4,,1,8,9, ,,1,,,,,3,, ,,,,,,,,, ,,,,22,240,,,, ,,,2,,,,,, ,,,5,,1,504,26,8,20 9,4,,,4,8,72,1720,1,26 1,14,,,,,5,1,3, ,,2,,1,1,3,36,7,1 ,7,2,2,,,,,, ,,,,,,,,,1 17,4,,,11,13,4458,8,16,2 ,,12,154,9,37,7,7,4,11 1,6,1,332,6,6,346,136,26,1 31,46,1,1,2,354,2477,36,,10 13,75,1,4,6,2,8,1,6,3 3,4,5,6,8,8,6,5,66,8 2,3,11,5,148,,,11672,59,2 110,,2,79,14,9,32,1234,304,1 ,,,,,,,5725,50,2 ,,,13,,7,8,,3,1 ,,,19,3,3,10216,1144,117,158 99602,263,,1,4,62,29,3,41,1 1,6,,1,1,,6,4,7,1 ,1,,,,,6,2,, ,5,14,4,220,,,33012,, ,,,,,,,,17,3 2,236,3,6,15,3,,,189,8 1917,1728,,19,,,2,,1,3 ,,,234,1,2,316,20,33,38 401,43,,,281,1742,148,22,,8 1,14,,,,,3,1,2, 2,,1,,3,3,3,1,5,1 ,3,2,35,,,,,, ,,2,,,1,2,,, ,1,,,,,,2,1,75 ,,,26,,,,,, ,,,45,,2,1632,80,26,8 41,12,2,,6,97,48,3,,1 ,4,,,,,1,3,1, ,14,,,,,4,1,1, ,3,7,2,2,,,8,, 1,,,,,,,,4, ,,,,,,,,, ,,,,,,,,, ,,,,,,,,, ,,,,,,,,, ,,,,,,,,, ,,,,,,,,, ,,,,,,,,, ,,,,,,,,, 207,497,355,123,19,9,146,4313,31,15 594,211,68,867,14,113,34,102,29,177 3,53,50,370,90,120,464,176,112,42 758,244,4,12,65,316,614,179,46,276 163,179,49,23,49,24,43,34,63,33 406,41,97,47,37,139,196,34,209,148 44,60,52,71,4790,5685,3,14571,2747,410 24266,576,722,554,522,720,1298,584,3439,12 ,,18,,,,,,, 23,1,1538,,,,,,, ,,,,,,,,, ,1,,,,59,20,,,13 4,2,3,,,,2,6,,1 ,,4,,,104,2,,10,108 21,1,1,1,2,,,1,5,2 2,,120,,,,3,,,33 11321,,104,255,,1,,7,1, 10,3,1,50209,4,2,10,13,9,6 ,29,5,402,7,23,581,86,887,218 47,43,453,19,1,26,6,5,5,8 8,8,1,1,2,2,3,5,4,3 11,4,5,3,1,8,12,3,5,8 4,96,7,36,149,,1,379,583,15 10,163,178,39923,3,568,4151,1630,1,1 ,,,,,,,,, ,,,,3607,,,,, ,,,1,,,,,, ,,,,,,,,5, ,,,,,,,,, ,,,,,,,,, ,,,,,,,,, ,,,,,,,,, ,15,,,,1,42,,1,4 ,,4,1,1,10645,2270,11372,4394,19 ,932,32,807,2,49,25,755,13,2 3,957,,210,8,205,2,33,,5 3,16,1,1,,1,5,13,4,1 9,2,4,42,22,7,11,1,31,116 12,370,7,394,4,,,3,1, 1,212,39,1,,51,64,14,,4 36,82,54,34,,,,,, 1892,470,3,19,,234,769,147,1066,62 ,200,220,411,1,62,13,22,21,1 25,938,,191,2,3,2,4,,4 3,11,2,1,1,1,5,3,109,28 5,2,7,2,2,4,6,5,315,3 241,262,16,54,61,,,4,69, 1,24,42,28,19,24,24,5,1,3 ,,,,,1,16,14,3,1 38,11,15,17,2,39,105,9259,161,15 ,44,10,20,2,6,4,8,2, 2,21,,20,6,8,12,4,1,6 4,14,4,1,1,1,3,2,7,2 6,2,5,2,2,37,42,1,20,15 17,39,3,29,129,68,,21,118, 10,526,74,,67,125,578,3,6,2 ,57,15,57,,,,,, 87,72,2,73,,8,25,827,443,1 ,49,4,8,,2,36,4,4,2 1,14,,1,2,1,8,5,,4 54,7,6,2,3,2,5,1,4,2 2,4,5,3,3,1,1,9,3364,108 93,4,1,87,139,,,41,73,135 8,636,25,97,,65,613,158,2,1 11,12,,,5,22,112,,2,2 20859,2899,31,39,3,5,16,1,30,14617 177,1862,904,3542,11,26,113,25,15,1 40,301,,42,139,211,96,118,16,120 75,211,5,26,16,13,20,32,50,7 24,57,38,74,37,51,23,5,218,41 1138,59,8,494,39,,,128,986, 7,128,30,25,,109,76,,,6 4,173,2,15,,,,,, ,,20,53,3,,,,,3 3,8,4,2,,,,,, ,5,,1,56,,7,14,,28 20,3,,1,2,4,6,2,6,1 ,21,10,16,,21,1,,6,7 ,1,,5,,,,,21, ,,,,,,,,,1 ,,,,,,,,, 105,34,3,,,,,,, ,32,2,,,,,,, ,,,,,,,1,,1 ,1,,,,,,,, 5,,3,,1,416,38,,1,16 92,7,1,,,,,,, ,2,,,,2,,,,6 ,,,,,,,,, 783,187,1,,,,,,,1 ,,119,,,,,,, ,1,,,,,3,1,,1 4,1,1,,1,4,1,,1, 1,1,1,34,1,,,2,528,28 102,1,,,,,,,, ,,,,,2,,,, 159,66,2,14,,1,10,5,41,4 958,500,10,3488,240,59,125,213,64,65 ,40,31,12052,8290,10266,837,426,432,21 147,1073,,47,31,565,12,36,20,52 112,75,23,4,4,12,15,14,70,8 276,22,189,58,12,116,225,10,157,21 17,318,578,435,139,73,1,135,4028,847 133,252,7556,425,48,33,1911,95,15,19 3,119,2,102,,,,3,4, 9,4,39,6972,111,258,90,253,144,132 34,164,179,1062,780,178,437,311,1051,83 444,1040,10,81,510,174,60,91,41,190 188,451,39,36,7,7,81,18,87,106 252,145,130,246,227,201,211,102,143,18 17,232,153,406,62,22,,18,1339,2 1,80,216,979,1,1,157,35,,12 19,30,1,10,1,1,10,20,8,2 13,2,56,1863,195,22,19,83,37,35 2,33,23,126,35,6089,193,31,198,65 25,77,2,18,30,42,34,39,194,55 40,71,13,10,17,14,32,26,34,29 147,26,62,21,22,385,117,19,79,126 35,103,40,77,551,336,3,313,3311,4261 783,467,8369,223,33,124,8226,444,34,14 189,6790,317,1732,63,126,499,592,118,69 748,488,77,2403,27,807,580,112,106,919 1,50,96,2225,504,1356,21439,12274,4730,2675 4126,3656,116,56,726,910,1623,885,12,83 168,915,6,10,16,8,135,194,45,12 84,102,176,75,125,165,1070,427,388,73 67,247,1527,435,223,18,1,1059,54,13 361,215,1535,88,15,73,5200,11,177,26 ,,,,,,,1,3, ,,42,81,1,5371,2944,1722,361,52 4,88,31,1529,194,39,923,1563,179,632 48,13586,,88,6,291,15,689,25,19 70,162,2,4,3,6,41,18,32,6 116,94,97,230,179,318,267,110,252,61 166,236,167,654,,,,,, ,,39,,,,,,,11 197,,,3,,,,6,1,1 94,45,2,1404,14,78,28,175,6,1 ,,,123,,2,483,162,3470,30 625,31,35,18,,37,1,1,7,16 14,16,,,,,1,1,2,2 1,,,,,1,1,2,7, 1,11,2,4,9,88,,32,193,10 18,843,396,164,,6,8626,53,9, ,,4,,,,,13,1, 2897,2304,7,2,,3,5,,2,294 ,166,130,23,41,27,164,75,67,137 10,59,,,25,76,28,44,253,157 116,313,9,28,22,9,32,2,13,7 82,59,70,140,42,52,10,26,651,80 179,33,3,138,38,62,,2,, 1,1,115,,642,1,22,2,,3 1000,6006,344,652,66,76,729,395,234,47 7670,3934,108,1474,56,271,117,282,50,654 7,96,74,1862,254,335,2729,731,562,228 10211,929,17,13,205,743,739,510,41,223 89,253,83,21,43,59,124,84,199,69 408,116,190,29,60,185,299,72,333,350 135,196,156,190,17282,262,1,16722,9508,973 967,693,2590,462,1635,332,3286,633,677,26 375,490,11,90,37,52,242,28,68,8 1332,1068,188,4014,424,431,525,331,527,410 1,956,672,2053,61,510,907,888,2006,52 260,4529,,465,83,236,117,396,28,416 284,1311,82,299,294,122,151,218,306,42 2099,462,535,878,469,2926,2642,471,7589,387 396,1176,1315,934,1336,158,,176,1983,108 505,661,1172,1687,193,159,3596,43,23,95 ,,,,,,,,, ,,,,,3,1,260,162,2 ,61,5,2,,5,,,, ,7,74,1188,,,,,, ,2,,,,,1,,, ,,,,,,,,, ,1,,60,,,,,, ,,6,,,,,1,, 55,,,4,,,,2,, 3,1,,5,,7,,7,2,9 ,,,1,1,2,7,1,1,1 5,11,,474,5,,3,,, ,1,,,,,,,,1 2,,,,,2,1,,7,2 ,5,1,53,9,1,,9,147,19 6,68,16,4,,23,40,30,1, 8,,,3,,,,9,1, 288,214,7,3940,,133,35,3,7,65 ,89,24,9,2,2,69,28,564,10 49,572,,1,1118,38,9,23,1,414 38,64,4,2,2,3,8,24,8,2 39,19,33,103,453,101,522,21,1606,14 184,141,223,23,20,4,,4,59,17 5,198,58,219,383,1,317,2,1,6 ,149,2,19,3,24,132,17,40,1 20842,5742,33,89,2,10,9,9,6,318 2,73,52,142,13,21,165,47,27,86 214,237,4,4,133,4489,450,167,7,93 44,178,94,19,84,23,224,62,152,209 144,108,178,99,54,103,442,32,502,117 331,94,79,102,91,28,,94,122,5 43,226,55,219,772,5,69,2,39,10 8,10,,,47,84,126,466,43,15 4855,1146,572,15,1,3,5,4,15,68 3,300,1281,46,138,96,337,25,21,47 44,625,,4,21,217,9272,475,2729,7095 6600,4631,1234,868,1973,777,1446,833,2098,918 473,1239,1360,1146,695,437,450,333,9978,1327 1538,469,81,514,148,1,,38,15,2 ,3,45,,,,13,2,,105 ,,,,3,10,69,1,16,3 3725,1033,457,8,,1,30,2,8,29 ,111,196,118,71,72,982,55,35,63 17,169,,2,50,241,1100,14133,1548,3689 1115,2276,461,109,97,153,402,295,660,442 1115,1088,1454,582,729,1681,1896,1179,2604,2174 505,672,429,906,90,110,,35,, ,6,62,20,,6,,8,,85 ,,7,,,,,1,, ,,,8858,1,,1,,1, ,,,4,3,10,667,111,793,464 402,8,,,,4,4,2,405,1 16,12,,,,,1,1,1, ,1,,,,,1,1,1, ,15,3,2,,,,7,181, ,,70,,,,,,, 9,14,,,,,,46,21,5 22247,7495,9,1,,,,,1,126 ,1,2,1,1,,7,1,13, 2,68,,,1,13,5,5,,713 4,24,157,4,246,99,94,127,163,3 43,138,92,5,3,501,81,23,37,99 1233,51,21,6,,,,4,28, 177,,,,,,7,4,1,18 36,,,1,10,17,257,,15,7 144,62,124,636,,1,2,3,5,410 1,87,207,14,3,6,37,4,177,18 2,216,,17,29,22,212,138,8,1069 1037,588,287,195,104,85,138,87,297,94 1265,450,429,332,366,686,976,244,12408,601 223,514,216,122,83,131,,101,2, 2,27,106,387,2961,17,201,62,3,39 140,239,31,42,43,41,179,499,69,7 6757,3358,212,951,90,8,7,36,8,1148 4,813,530,509,107,123,296,65,373,50 324,422,,56,17,409,560,458,137,1374 548,1717,189,116,198,139,184,245,319,238 969,463,313,391,286,1004,1214,496,3671,554 394,473,232,334,619,55,,240,477,23 14,153,404,95,2016,27,249,17,4,48 ,,18,51,28,28,221,216,57,20 ,,57,,,,,,1,2 ,1,3,1,3,,14,,, 10,25,,,,7,33,3,,28 15,77,1443,871,566,116,14,109,155,53 5,69,149,5,3,8,3,5,1617,17 974,8,,5,274,215,,1501,1, 6,,138,1,78,,,11,15,34 385,882,19,75,,,,,, ,,,30,,,,,,2 ,,,,1,,1,,, ,,,,,,3,,,2 1,3,2,356,11,1,1,1,1, ,,,,,,,,1,1 3,,,,19,,,77,19, 90,,309,,2,4,,,39, ,,,,48,61,805,113,140,22 432,50,,,,,,,,5 ,,,,,,9,,, 6,1,,,,134,3,,,3 ,9,11,2,566,11,1,1,7,1 ,,2,,,1,,,18, 46,2,,,,,,7,, 2,,261,,,,1,2,,5 ,,,,18,13,119,,45,4 1221,475,2,3,1,32,,3,1,17 ,,1,,,,2,2,, ,6,,,,5,12,12,,2 1,1,,1,2,345,2,10,4,1 1,,1,,,,,,23, 4,1,,,8,,,10,134, 2,,229,,,,,3,,3 ,,,3,2,3,5,35,7, 57,19,124,46,3,15,8,14,7,113 1,14,48,34,78,15,33,13,18,2 10,206,1,7,51,33,370,499,50,220 394,322,188,67,124,67,937,163,308,285 316,173,188,62,102,246,252,109,482,618 113,135,30,37,83,2,,37,73, 3,,183,,13,1,,5,6,23 ,,,,,,,1,, ,,1,156,,166,129,85,16,43 1,10,1,295,101,322,595,53,10,2 11,185,,3,72,6,56,23,,3 3,20,,1,1,2,4,380,5, 5,2,1,,,1,3,1,2, 5,3,4,18,,,,,21, 1,,341,92,,,,,,7 21,28,9,11,40,30,442,340,72,12 1367,221,184,123,21,1,2,6,3,46 ,13,76,155,2,9,349,180,141,6 201,77,,1,14,98,700,430,7,384 65,144,357,469,1082,556,383,567,2087,230 113,614,139,101,15,377,54,113,1170,478 637,58,82,65,526,92,,188,25,25 19,,773,1,43,,,15,3,46 57,89,2,9,5,16,119,203,33,5 206,76,156,165,3,52,26,46,17,127 3,29,51,96,42,33,80,37,31,11 36,290,2,14,65,88,349,225,38,252 1071,329,451,207,122,149,504,397,474,1382 265,311,175,58,56,373,236,79,1860,759 303,189,61,71,272,25,,223,424, 33,40,181,297,780,6,20,24,81,34 ,,,1,,,,,, ,,10,8,1,2,1,2,3,1 ,1,11,10,,31,12,2,5, 2,16,,,1,2,12,2,,10 12,27,3,1,3,5,7,5,19,4 9408,3,46,6,7,335,375,12,7,31 4,67,31,6,10,,,6,177,31 14,788,546,10,2,10,35,9,,72 ,,,,,,,,, 4047,1852,2,61,,,,,1,21 ,,2,1,,,86,4,3,1 2,2,,,,1,6,6,1,96 15,9,3,2,2,2,3,8,11,1 3,992,5,262,3,13,1,7,1932,3 117,4,,24,11,,,11,387, 2,38,38,295,735,29,24,16,4,1 12,13,,1,6,13,118,334,28,7 2365,870,140,2,,,1,,2,30 ,2,47,7,1,5,56,2,10,1 15,23,,,28,18,474,199,2,331 66,206,146,80,186,244,356,480,780,73 2382,1267,2076,705,250,692,351,221,368,135 204,410,85,89,267,,,525,12,1 53,21,269,,152,4,,1,162,53 ,,,,,,,1,, 1052,544,1,,,,,43,, ,,,,,,,,, ,,,,9,,,,,2 3,,1,,,,,,1, ,3,1,140,,,,,2,1 203,,,,24,,,30,36,53 55,633,78,1,1,,28,9,14, 6,6,1,6,,2,36,31,3,1 5440,1911,12,25,,5,2,6,2,25 ,4,7,9,2,7,10,3,6,1 14,106,,1,18,47,17,25,1,20 45,15,5,32,4,3,34,4,9,4 258,154,247,262,389,725,212,137,1163,11 262,93,44,51,94,25,,268,104,42 92,39,243,99,308,38,327,32,28,14 ,,,,,,1,5,, 1446,464,1497,3,,,,1,,1 ,2,3,2,1,6,1,1,1, ,8,,,1,1,5,7,,19 12,16,20,1,4,3,6,11,9,2 42,5,31,1,7,3314,86,3,798,2469 201,24,9,11,76,2091,2,13,95,32 1,35,71,3,82,6,81,11,2,84 ,,,,,,,,, ,,528,,,14,3,1,3,2 ,8,9,18,,17,36,7,18,1 ,82,,1,3,9,12,58,,30 25,74,20,1,8,7,30,69,93,15 6515,8,953,12,329,15400,9168,743,465,1548 23,1009,1678,127,74,727,28,16,18,96 19,915,2002,,,,214,2,6,300 94,448,7,49,,4,11,12,6,1 308,96,12,12,,1,,4,1,15 ,,2,6,1,7,4,,6,1 2,16,,,1,3,12,22,,8 24,14,207,87,7,10,8,2,6,94 42,5,23,4,312,42,19,680,3356,168 79,26,21,10,84,15,,31,173,27 90,12,147,15,609,4,554,14,10,6 59,71,6,73,38,5,8,8,46,2 315,137,52,18,1,2,,13,9,54 ,2,32,25,4,12,15,2,7,15 136,39,,,1,88,101,58,2,75 373,85,156,189,244,26,59,20,37,44 32,58,74,35,39,99,9,132,48021,57 739,34,4,13,438,251,,123,568,48 39,19,196,28,10344,37,99,72,33,10 ,,,9,,,,,, ,,2244,,1,,,,, ,,4,,,9,,,, 1,20,,,,2,9,2,,33 22,29,74,3,14,10,14,21,29,52 55,14,77,,12,209,31,,55,10609 111,33,2,12,1991,,,31,, 1,,2,,,,,,,78 ,,563,14,1,3,,,, 4,,2,,,,,,1,3 ,,1,,,,,,, ,,,,,,4,,,3 1,1,3,1,2,,1,,2, ,,4,,,,1,,47,1 952,,,10,1412,12,,108,3,70 15,7,397,1,15,256,15,22,35,1 ,,3,1,,2,16,41,2, 1158,506,17,58,3,10,2,10,4,15 1,8,6,37,5,7,99,28,57,2 36,78,,3,26,13,94,40,2,78 16,27,6,3,4,4,27,7,54,7 62,255,43,386,7,135,27,13,360,458 65,447,34,11,54,6,,173,121,7 4,35,72,,18,,3687,5,3,9 ,,1,8,,,1,4,1, 19,7,60,17,1,4,5,8,1,8 ,2,2,22,6,618,22,5,14,1 7,21,,,6,14,11,4,3,11 8,18,9,2,5,4,9,9,13,10 56,9,26,3,16,199,66,9,25,290 13,28,848,18,51,24,18,75,327,248 91,566,2373,7,3,181,1201,21,7,12 9,14,,7,3,1,6,7,5,1 590,196,9,20,1,8,2,867,40,12 ,22,2,8,3,110,14,3,25,6 2,32,,77,1,25,8,6,1,25 5,18,1,1,8,5,5,3,5,3 52,38,17,83,3,25,18,2,46,13 34,30,6,1011,107,54,,91,742,326 91,902,679,198,19,110,689,111,17,3 1315,975,105,486,58,58,275,369,107,26 6625,2280,388,6714,126,300,316,504,160,1073 13,317,270,2273,768,1533,4136,1025,1127,400 7634,1821,30,96,450,2781,1844,1593,293,751 531,752,173,145,204,87,252,191,350,276 1277,329,487,222,274,964,1218,407,1650,1238 358,378,538,629,31372,555,16,4809,14477,2836 774,461,6962,2493,2183,773,4995,3892,216,72 207,312,11,15,2,4,26,94,13,2 1401,693,118,659,12,160,30,510,54,84 2,87,64,170,53,696,237,68,195,27 234,213,2,33,43,126,71,91,12,173 180,198,37,21,83,30,65,80,155,77 427,77,132,40,45,463,324,43,106,334 87,172,158,188,1914,1999,10,779,10089,5704 1142,1176,7161,826,910,440,4101,250,44,21 ,,,,,,,,, ,,,,,,,,, ,,,,,,,,, ,,,,,,,,,8 ,,,,,,,,, ,,,,,,,,, ,,,,,,12,,, 3,,531,,,,,,, 797,2774,80,372,408,296,840,1710,560,360 1609,613,447,5913,88,1205,419,715,228,1274 13,237,276,4204,374,792,7755,1849,841,145 5131,2441,34,69,1278,2597,4220,2828,239,651 658,1194,174,136,235,101,365,207,415,321 928,295,517,311,252,962,1359,330,1767,986 334,441,340,396,4173,1383,17,51703,20996,4133 7732,4586,4196,4927,1588,1466,8982,759,3442,54 2513,4120,392,1253,106,122,994,616,227,47 26087,9600,475,17260,199,1371,624,2240,738,2509 35,830,626,4088,440,2443,3873,1461,2114,496 2718,2547,86,222,558,1221,2701,2948,477,1960 1239,1787,578,711,1042,447,657,706,1127,406 3086,1253,1600,864,843,3421,3198,800,7188,1384 1377,1231,896,1525,5404,471,7,3136,10189,1044 1609,1560,4898,8768,11095,513,6175,560,221,151 1478,1518,76,260,19,53,271,560,102,102 2501,633,120,1889,145,212,81,550,159,397 12,318,296,363,66,650,938,178,551,80 1337,498,7,129,122,380,317,379,76,297 284,421,88,38,87,44,137,128,178,272 962,92,364,139,202,516,710,251,683,1058 147,216,382,324,3831,1353,16,2096,10827,65279 14742,2034,4833,3799,520,646,3105,115,65,33 1676,10760,139,1030,45,70,904,9422,228,40 1012,396,183,1472,59,130,72,587,194,268 6,179,176,408,137,1317,883,357,1173,85 857,806,4,62,207,382,273,260,90,380 206,359,58,62,71,68,169,108,210,223 867,114,357,68,114,3645,664,195,345,459 524,344,247,492,3650,2457,87,2100,36419,8924 49610,4210,14526,6373,1963,2822,38336,1212,151,76 132,154,16,42,3,7,34,70,49,8 475,123,61,830,12,54,26,202,72,91 3,59,48,261,30,453,236,92,182,44 75,158,15,37,35,85,115,103,33,245 73,123,26,12,25,17,45,36,66,59 1199,63,145,53,89,559,537,106,136,872 50,85,124,88,600,263,12,417,3351,1754 212,1279,3770,969,220,474,2582,130,37,24 374,2040,154,637,87,107,1084,1449,329,92 46412,3386,1067,12160,1268,1448,418,1806,463,1036 26,842,925,2144,380,4572,4129,1539,9796,499 3114,2360,28,337,452,1305,1200,1125,224,1297 1060,1524,323,203,446,240,573,417,838,1223 3585,637,938,878,541,4241,2842,549,4116,3290 680,1330,1552,1529,10966,4755,80,3737,82453,30351 13701,5919,39370,8619,3355,5151,25392,1425,325,173 14,20,43,120,4,10,41,425,27,13 190,61,190,712,24,137,43,245,66,155 5,87,76,185,75,1262,526,178,637,82 342,406,3,43,62,206,122,155,56,218 102,212,45,28,61,39,113,74,205,116 779,92,185,71,92,650,673,99,211,1167 89,249,297,163,2202,384,20,517,11535,2594 1466,556,5472,278,197,552,3557,148,111,32 249,297,46,276,37,64,210,239,50,22 950,178,32,695,39,65,30,142,36,245 3,100,95,218,75,288,141,132,110,33 137,177,2,18,68,181,60,120,24,125 135,130,46,10,20,33,72,26,73,88 148,25,58,26,30,113,256,56,4928,146 56,262,96,80,4511,259,1,545,11956,1009 424,687,3752,317,429,682,2597,388,33,12 ,,,352,,,2,8,3,1 2,1,1,7,2,,,4,,1 ,18,8,5,1,16,12,2,10,1 3,6,,,2,1,1,3,1,11 1,3,2,,1,2,2,,8,1 4,,2,,1,14,39,31,28,32 27,33,22,11,82,6,343,26,1565,5 3,31,4806,1706,,6802,822,50,,1 596,44,19,44,5,9,47,58,10,3 10,5,14,155,11,48,4,44,66,80 3,43,25,62,5,149,134,52,274,16 118,173,1,4,11,39,14,37,12,42 68,43,7,4,5,4,25,8,32,34 60,23,17,17,12,115,125,9,207,124 13,81,121,88,445,152,5,317,886,1294 253,680,1936,366,59,323,6960,30,17,5 10,15,8,81,3,8,15,23,8,7 236,72,23,447,50,60,35,292,45,50 2,45,50,100,33,1183,113,30,144,33 113,95,3,58,29,61,72,52,5,73 39,77,16,18,21,18,22,36,55,28 117,15,58,58,28,369,131,16,224,239 33,80,52,110,333,426,2,829,3763,7195 1425,286,3667,291,55,104,2381,618,39,11 ,,4,33,1,3,5,11,11,2 136,40,9,308,5,22,12,15,1,11 ,5,3,82,8,20,49,13,30, 51,20,6,1,6,15,19,17,5,12 10,24,3,2,4,2,7,3,7,5 29,6,8,5,5,20,28,8,44,16 4,12,9,10,212,107,2,57,515,80 109,121,146,153,84,24,278,31,3,1 1,7,4,5,13,75,39,513,14,12 1,,14,4353,5,74,278,69,98,20 ,26,7,1071,23,45,895,134,560,70 826,959,1,1,183,115,1184,2807,10,114 45,162,76,72,114,23,140,67,65,16 270,12,163,55,54,473,142,86,1360,81 14,135,93,905,7080,2598,1,341,1649,2544 34,96,1139,126,142,129,65,856,1,21 20358,41531,3706,10213,1118,860,12999,94659,4147,920 156635,67036,11870,74833,14205,8379,3231,19409,4403,17496 180,5740,7022,24423,8988,33375,29925,9466,23680,3645 34479,26857,776,2432,6524,17896,26394,13890,4852,16649 14164,21872,7167,4945,9244,3139,12495,7637,11631,10223 17064,7738,17112,5474,7580,24778,9758,7457,57221,34493 12227,14622,9679,10959,132337,79652,966,147758,475550,165728 517420,61039,322773,84779,48837,25330,256578,16653,5065,455017 * End of file "econ.dat" games.dat0000444000175000017500000003326505407367743011014 0ustar dondon* File "games.dat" from the Stanford GraphBase (C) 1993 Stanford University * College football teams and scores, 1990 * This file may be freely copied but please do not change it in any way! * (Checksum parameters 787,776511596) USAF Air Force(Falcons)Western Athletic;,;5, AKRON Akron(Zips)Independent;,;, BAMA Alabama(Crimson Tide)Southeastern;104,;13,1 AZ Arizona(Wildcats)Pacific Ten;370,48;, ASU Arizona State(Sun Devils)Pacific Ten;113,8;, ARK Arkansas(Razorbacks)Southwest;647,79;, ARMY Army(Cadets)Independent;,;, AUBN Auburn(Tigers)Southeastern;1385,619;288,39 BALL Ball State(Cardinals)Mid-American;,;, BAYL Baylor(Bears)Southwest;,;, BOST Boston College(Eagles)Independent;,;, BOWLG Bowling Green(Falcons)Mid-American;,;, BYU Brigham Young(Cougars)Western Athletic;1171,430;246,41 BROWN Brown(Bears)Ivy;,;, BUCK Bucknell(Bisons)Patriot;,;, CAL California(Golden Bears)Pacific Ten;,;37, CMICH Central Michigan(Chippewas)Mid-American;,;, CINCI Cincinnati(Bearcats)Independent;,;, CLEM Clemson(Tigers)Atlantic Coast;471,21;950,420 COLG Colgate(Red Raiders)Patriot;,;, COLO Colorado(Buffaloes)Big Eight;1041,305;1475,846 CSU Colorado State(Rams)Western Athletic;9,;67, COLUM Columbia(Lions)Ivy;,;, CORN Cornell(Big Red)Ivy;,;, DART Dartmouth(Big Green)Ivy;,;, DUKE Duke(Blue Devils)Atlantic Coast;,;, ECAR East Carolina(Pirates)Independent;,;, EMICH Eastern Michigan(Hurons)Mid-American;,;, FLA Florida(Gators)Southeastern;163,18;863, FSU Florida State(Seminoles)Independent;1367,647;1303,677 FORD Fordham(Rams)Patriot;,;, FRES Fresno State(Bulldogs)Big West;51,12;, FULL Fullerton State(Titans)Big West;,;, GA Georgia(Bulldogs)Southeastern;7,;, GTECH Georgia Tech(Yellow Jackets)Atlantic Coast;37,7;1441,847 HARV Harvard(Crimson)Ivy;,;, HI Hawaii(Rainbow Warriors)Western Athletic;,;2, HOLY Holy Cross(Crusaders)Patriot;,;, HOUS Houston(Cougars)Southwest;395,;940, ILL Illinois(Fighting Illini)Big Ten;365,25;146,6 IND Indiana(Fightin' Hoosiers)Big Ten;,;, IOWA Iowa(Hawkeyes)Big Ten;,;371,57 ISU Iowa State(Cyclones)Big Eight;,;, KAS Kansas(Jayhawks)Big Eight;,;, KSU Kansas State(Wildcats)Big Eight;,;, KENTS Kent State(Golden Flashes)Mid-American;,;, KY Kentucky(Wildcats)Southeastern;,;, LAFAY Lafayette(Leopards)Patriot;,;, LHIGH Lehigh(Engineers)Patriot;,;, LBSU Long Beach State(Forty-Niners)Big West;,;, LSU Louisiana State(Fighting Tigers)Southeastern;25,;, LTECH Louisiana Tech(Bulldogs)Independent;,;, LOUVL Louisville(Cardinals)Independent;5,;775,245 MD Maryland(Terps)Atlantic Coast;42,2;, MEMPH Memphis State(Tigers)Independent;,;, MIAMI Miami(Hurricanes)Independent;1013,290;1388,763 MIOH Miami of Ohio(Redskins)Mid-American;,;, MICH Michigan(Wolverines)Big Ten;1230,462;1025,426 MSU Michigan State(Spartans)Big Ten;382,15;610,120 MINN Minnesota(Golden Gophers)Big Ten;,;, MISS Mississippi(Rebels)Southeastern;3,;253,7 MSSU Mississippi State(Bulldogs)Southeastern;,;, MO Missouri(Tigers)Big Eight;,;, NAVY Navy(Midshipmen)Independent;,;, NEB Nebraska(Cornhuskers)Big Eight;1047,421;185,41 UNLV Nevada-Las Vegas(Rebels)Big West;,;, NMEX New Mexico(Lobos)Western Athletic;,;, NMSU New Mexico State(Aggies)Big West;,;, NCAR North Carolina(Tar Heels)Atlantic Coast;,;4, NCSU North Carolina State(Wolfpack)Atlantic Coast;,;30, NIL Northern Illinois(Huskies)Independent;,;, NW Northwestern(Wildcats)Big Ten;,;, NDAME Notre Dame(Fighting Irish)Independent;1451,666;1179,548 OSU Ohio State(Buckeyes)Big Ten;467,114;7,1 OU Ohio University(Bobcats)Mid-American;,;, OK Oklahoma(Sooners)Big Eight;662,;452, OKSU Oklahoma State(Cowboys)Big Eight;,;, OR Oregon(Ducks)Pacific Ten;36,2;6,1 ORSU Oregon State(Beavers)Pacific Ten;,;, PAC Pacific(Tigers)Big West;,;, PSU Penn State(Nittany Lions)Independent;25,;907,301 PENN Pennsylvania(Red \& Blue)Ivy;,;, PITT Pittsburgh(Panthers)Independent;673,140;, PRIN Princeton(Tigers)Ivy;,;, PURD Purdue(Boilermakers)Big Ten;1,;, RICE Rice(Owls)Southwest;,;, RUTG Rutgers(Scarlet Knights)Independent;1,;, SDSU San Diego State(Aztecs)Western Athletic;,;, SJSU San Jose State(Spartans)Big West;,;138,16 SCAR South Carolina(Fighting Gamecocks)Independent;40,2;, USC Southern California(Trojans)Pacific Ten;1126,479;266,9 SMU Southern Methodist(Mustangs)Southwest;,;, SMISS Southern Mississippi(Golden Eagles)Independent;31,2;48,1 SWLA Southwestern Louisiana(Ragin' Cajuns)Independent;,;, STAN Stanford(Cardinal)Pacific Ten;4,;, SYR Syracuse(Orangemen)Independent;2,;121,12 TEMP Temple(Owls)Independent;,;, TENN Tennessee(Volunteers)Southeastern;1108,441;993,449 TEX Texas(Longhorns)Southwest;214,25;887,268 TA&M Texas A\&M(Aggies)Southwest;802,188;627,204 TCU Texas Christian(Horned Frogs)Southwest;,;, UTEP Texas-El Paso(Miners)Western Athletic;,;, TTECH Texas Tech(Red Raiders)Southwest;,;, TOL Toledo(Rockets)Mid-American;,;, TUL Tulane(Green Wave)Independent;,;, TULSA Tulsa(Golden Hurricane)Independent;,;, UCLA UCLA(Bruins)Pacific Ten;38,;6, UTAH Utah(Utes)Western Athletic;,;, USU Utah State(Aggies)Big West;,;, VAND Vanderbilt(Commodores)Southeastern;,;, VA Virginia(Cavaliers)Atlantic Coast;1005,272;188,65 VTECH Virginia Tech(Gobblers)Independent;,;5,5 WAKE Wake Forest(Demon Deacons)Atlantic Coast;,;, WASH Washington(Huskies)Pacific Ten;345,20;1246,664 WSU Washington State(Cougars)Pacific Ten;,;, WVA West Virginia(Mountaineers)Independent;10,;, WMICH Western Michigan(Broncos)Mid-American;,;, WIS Wisconsin(Badgers)Big Ten;,;, WYO Wyoming(Cowboys)Western Athletic;16,;7, YALE Yale(Bulldogs)Ivy;,;, >A26 COLO31,TENN31 >A31 USC34,SYR16 >S1 CSU35@USAF33 LBSU0@CLEM59 LTECH17@ECAR27 EMICH10@FRES41 TA&M28@HI13 VA59@KAS10 CMICH17@KY20 VTECH13@MD20 BAYL0@NEB13 NMEX29@NMSU12 MIOH0@NCAR34 TULSA3@OKSU10 OU3@PITT35 WAKE17@RICE33 LOUVL10@SJSU10 DUKE10@SCAR21 PAC7@TENN55 WSU21@TCU3 BYU30@UTEP10 SWLA48@TUL6 UTAH19@USU0 KENTS24@WVA35 TEMP23@WYO38 >S2 BOWLG34@CINCI20 >S6 STAN17@COLO21 >S8 HI3@USAF27 SMISS27@BAMA24 ILL16@AZ28 BAYL13@ASU28 FULL17@AUBN38 MIAMI21@BYU28 CINCI34@CMICH0 WMICH27@EMICH24 OKSU7@FLA50 ECAR24@FSU45 NMEX17@FRES24 NCSU13@GTECH21 UNLV9@HOUS37 ORSU12@KAS38 AKRON10@KENTS38 FORD3@LHIGH35 GA13@LSU18 TOL14@MIOH20 UTAH35@MINN29 MEMPH21@MISS23 TENN40@MSSU7 TCU20@MO19 NIL14@NEB60 TTECH10@OSU17 SDSU21@OR42 TEX17@PSU13 BOST6@PITT29 TUL21@RICE10 KY8@RUTG24 NCAR5@SCAR27 VAND7@SMU44 TEMP9@SYR19 NMSU27@UTEP24 OK34@UCLA14 LBSU13@USU27 CLEM7@VA20 BOWLG21@VTECH7 SJSU17@WASH17 WYO34@WSU13 MD14@WVA10 CAL28@WIS12 >S13 HOUS51@TTECH35 >S15 CMICH14@AKRON14 FLA17@BAMA13 CSU20@ASU31 TULSA3@ARK28 HOLY7@ARMY24 OSU31@BOST10 WSU36@BYU50 YALE27@BROWN21 MIAMI52@CAL24 HARV9@COLUM6 PRIN14@CORN17 PENN16@DART6 VTECH24@ECAR23 OU18@EMICH45 SMISS17@GA18 COLO22@ILL23 CINCI10@IOWA63 NMSU7@KSU52 IND45@KY24 BUCK24@LAFAY14 MIOH7@LSU35 KAS16@LOUVL28 CLEM18@MD17 ISU16@MINN20 AUBN24@MISS10 FULL13@MSSU27 USU10@MO45 AZ25@NMEX10 WAKE15@NCSU20 DUKE27@NW24 MICH24@NDAME28 PITT28@OK52 UNLV19@ORSU20 SJSU28@PAC14 WASH20@PURD14 COLG17@RUTG28 LBSU20@SDSU38 PSU14@USC19 MSU23@SYR23 UTEP0@TENN56 SWLA14@TA&M63 NIL14@TOL23 SMU7@TUL43 STAN31@UCLA32 FRES31@UTAH7 NAVY14@VA56 LTECH21@WMICH27 BALL7@WIS24 >S22 FULL17@AKRON48 OR17@AZ22 MISS21@ARK17 TOL28@BALL16 SDSU34@BYU62 COLUM16@BUCK41 BOWLG0@CMICH17 MIOH16@CINCI12 CORN24@COLG59 UTEP20@CSU38 LHIGH14@DART33 VA59@DUKE0 NMSU3@FRES42 BAMA16@GA17 MO7@IND58 ISU35@IOWA45 PAC7@LBSU28 NCSU12@MD13 UCLA15@MICH38 NDAME20@MSU19 SMISS10@MSSU13 MINN0@NEB56 SJSU47@UNLV13 TTECH34@NMEX32 KY13@NCAR16 KSU35@NIL42 TULSA10@OK52 RUTG0@PSU28 HOLY17@PENN3 FORD14@PRIN23 NW14@RICE31 ECAR20@SWLA10 ORSU3@STAN37 PITT20@SYR20 COLO29@TEX22 OKSU21@TCU31 FSU31@TUL13 HI19@UTAH7 LSU21@VAND24 SCAR35@VTECH24 USC0@WASH31 CAL31@WSU41 LOUVL9@WVA7 KENTS10@WMICH37 TEMP24@WIS18 USAF12@WYO24 LAFAY17@YALE18 >S29 VAND28@BAMA59 CAL20@AZ25 CSU52@ARK31 TENN26@AUBN26 BOWLG6@BALL16 FORD35@BROWN28 MIOH7@CMICH31 DUKE7@CLEM26 PRIN13@COLG39 WASH14@COLO20 BUCK42@CORN21 MSSU21@FLA34 VTECH28@FSU39 FULL3@FRES38 ECAR15@GA19 SCAR6@GTECH27 HARV14@HOLY35 RICE22@HOUS24 EMICH6@IND37 WMICH20@ISU34 NMEX6@KSU38 CINCI27@KENTS24 PENN13@LAFAY20 COLUM9@LHIGH42 TA&M8@LSU17 SWLA10@LTECH24 IOWA21@MIAMI48 MD17@MICH45 TUL21@MISS31 ASU9@MO30 BOST28@NAVY17 ORSU7@NEB31 NCSU12@NCAR9 NIL7@NW24 PURD11@NDAME37 USC35@OSU26 TOL27@OU20 KAS17@OK31 BYU16@OR32 UNLV37@PAC28 WVA38@PITT24 MSU34@RUTG10 USAF18@SDSU48 TCU42@SMU21 LOUVL13@SMISS25 SJSU29@STAN23 BAYL21@TTECH15 MEMPH22@TULSA10 WYO28@UTAH10 ARMY14@WAKE52 UCLA30@WSU20 >O6 NAVY7@USAF24 WMICH24@AKRON20 WASH42@ASU14 TCU54@ARK26 DUKE17@ARMY16 LTECH14@AUBN16 HOUS31@BAYL15 RUTG14@BOST19 OU10@BOWLG10 SJSU34@CAL35 GA3@CLEM34 UTAH13@CSU22 LAFAY41@COLUM34 SMISS16@ECAR7 LSU8@FLA34 CORN20@HARV17 DART10@HOLY21 KAS34@ISU34 NEB45@KSU8 CMICH42@KENTS0 NMSU27@LBSU31 TULSA14@LOUVL38 GTECH31@MD3 TUL14@MEMPH21 FSU22@MIAMI31 BALL10@MIOH24 IOWA12@MSU7 KY29@MISS35 COLO33@MO31 FULL10@UNLV29 UTEP28@NMEX48 FRES18@NIL73 IND42@NW0 STAN36@NDAME31 ILL31@OSU20 OK31@OKSU17 USU7@OR52 TEMP10@PSU48 LHIGH22@PENN16 BROWN23@PRIN27 MINN19@PURD7 TEX26@RICE10 WSU17@USC30 BAMA25@SWLA6 TTECH24@TA&M28 EMICH23@TOL37 AZ28@UCLA21 SYR49@VAND14 WVA21@VTECH26 NCAR31@WAKE24 MICH41@WIS3 SDSU51@WYO52 COLG30@YALE7 >O13 CAL31@ASU24 TTECH49@ARK44 VAND6@AUBN56 KENTS0@BALL31 SMU17@BAYL52 ARMY20@BOST41 CSU9@BYU52 HOLY55@BROWN0 PRIN9@BUCK14 WMICH13@CMICH20 ISU12@COLO28 YALE17@DART27 USU24@FRES24 PAC67@FULL37 MISS28@GA12 CLEM19@GTECH21 FORD13@HARV19 TA&M31@HOUS36 PURD0@ILL34 OSU27@IND27 WIS10@IOWA30 OKSU17@KSU23 MSSU15@KY17 CORN38@LAFAY16 MEMPH17@LOUVL19 WAKE13@MD41 KAS0@MIAMI34 MSU28@MICH27 NW25@MINN35 AKRON13@NAVY17 MO21@NEB69 UNLV24@NMSU20 USAF27@NDAME57 MIOH40@OU18 TEX14,OK13 AZ21@ORSU35 SYR21@PSU27 COLUM6@PENN21 RUTG21@PITT45 LBSU29@SJSU46 ECAR7@SCAR37 USC37@STAN22 FLA3@TENN45 RICE28@TCU38 HI10@UTEP12 BOWLG13@TOL19 SMISS14@TUL14 LTECH35@TULSA21 SDSU31@UCLA45 NCSU0@VA31 OR17@WASH38 CINCI20@WVA28 NMEX22@WYO25 >O20 LAFAY0@ARMY56 FSU17@AUBN20 PSU40@BOST21 EMICH15@BOWLG25 PENN17@BROWN24 UCLA31@CAL38 TOL12@CMICH13 NMEX7@CSU47 DART11@CORN6 MD23@DUKE20 CINCI32@ECAR56 AKRON0@FLA59 COLG31@FORD7 LBSU16@FRES28 VAND28@GA39 FULL21@HI45 LHIGH22@HOLY34 MSU13@ILL15 COLO41@KAS10 OU15@KENTS44 KY20@LSU30 IOWA24@MICH23 IND0@MINN12 KSU10@MO31 OKSU3@NEB31 GTECH13@NCAR13 CLEM24@NCSU17 WIS34@NW44 MIAMI20@NDAME29 ISU33@OK31 ASU7@OR27 WSU55@ORSU24 NMSU24@PAC62 LOUVL27@PITT20 HARV23@PRIN20 OSU42@PURD2 TTECH21@RICE42 USU27@SJSU34 AZ35@USC26 HOUS44@SMU17 MEMPH7@SMISS23 WASH52@STAN16 RUTG0@SYR42 VTECH28@TEMP31 BAMA9@TENN6 ARK17@TEX49 BAYL20@TA&M20 MSSU38@TUL17 SWLA25@TULSA13 UTEP23@UTAH37 VA49@WAKE14 BALL13@WMICH14 COLUM7@YALE31 >O27 UTAH21@USAF52 PSU9@BAMA0 WSU34@AZ42 USC13@ASU6 SYR26@ARMY14 MIOH10@BOWLG10 NMEX31@BYU55 COLG28@BUCK27 OK23@COLO32 PRIN15@COLUM17 BROWN7@CORN34 HARV0@DART17 CMICH16@EMICH12 LSU3@FSU42 DUKE31@GTECH48 PAC24@HI35 LAFAY3@HOLY34 ARK28@HOUS62 MICH45@IND19 NW14@IOWA56 NEB45@ISU13 KSU24@KAS27 GA24@KY26 FULL35@LBSU37 SWLA6@MEMPH20 PURD33@MSU55 AUBN17@MSSU16 TULSA35@NMSU10 MD10@NCAR34 SCAR29@NCSU38 MINN23@OSU52 WMICH31@OU23 MO28@OKSU48 STAN0@OR31 YALE27@PENN10 NDAME31@PITT22 AKRON17@RUTG20 ECAR27@TEMP30 SMU3@TEX52 RICE15@TA&M41 BAYL27@TCU21 WYO17@UTEP10 MIAMI45@TTECH10 KENTS14@TOL28 CINCI7@TUL49 ORSU17@UCLA26 UNLV6@USU31 MISS14@VAND13 SMISS16@VTECH20 CLEM24@WAKE6 CAL7@WASH46 BOST27@WVA14 ILL21@WIS3 >N3 BYU54@USAF7 NIL31@AKRON28 ORSU9@ASU34 RICE19@ARK11 RUTG31@ARMY35 SYR35@BOST6 BALL13@CMICH3 LOUVL41@CINCI16 NCAR3@CLEM20 WYO8@CSU31 COLUM20@DART34 WAKE20@DUKE57 AUBN7@FLA48 LAFAY59@FORD14 BROWN37@HARV52 NMEX16@HI43 BUCK14@HOLY43 TCU35@HOUS56 IOWA54@ILL28 ISU14@KSU28 BOWLG20@KENTS16 COLG7@LHIGH52 MISS19@LSU10 ECAR24@MEMPH17 PITT0@MIAMI45 EMICH14@MIOH34 IND20@MSU45 BAMA22@MSSU0 OK55@MO10 NDAME52@NAVY31 COLO27@NEB12 FRES45@UNLV18 NW7@OSU48 KAS31@OKSU30 UCLA24@OR28 PENN20@PRIN34 MICH38@PURD13 UTAH14@SDSU66 FULL6@SJSU44 FSU41@SCAR10 CAL31@USC31 TA&M38@SMU17 SMISS14@SWLA13 WSU13@STAN31 TEMP20@TENN41 TEX41@TTECH22 NMSU10@USU55 GTECH41@VA38 NCSU16@VTECH20 AZ10@WASH54 PSU31@WVA19 TOL37@WMICH9 MINN21@WIS3 CORN41@YALE31 >N10 LSU3@BAMA24 STAN23@AZ10 USAF15@ARMY3 SMISS13@AUBN12 ARK3@BAYL34 WMICH19@BOWLG13 DART29@BROWN0 OR3@CAL28 LAFAY7@COLG36 OKSU22@COLO41 TULSA13@CSU31 CORN41@COLUM0 NIL20@ECAR24 BALL20@EMICH13 GA7@FLA38 CINCI21@FSU70 HOLY48@FORD0 PAC17@FRES48 USU45@FULL17 VTECH3@GTECH6 WIS7@IND20 OSU27@IOWA26 MO25@ISU27 NEB41@KAS9 VAND21@KY28 BUCK30@LHIGH27 AKRON15@LTECH36 BOST10@LOUVL17 MSSU27@MEMPH23 KENTS10@MIOH31 ILL17@MICH22 MSU28@MINN16 UTAH29@NMEX27 SJSU56@NMSU20 VA24@NCAR10 DUKE0@NCSU16 PURD33@NW13 CMICH52@OU7 KSU7@OK34 USC56@ORSU7 MD10@PSU24 HARV20@PENN24 TEMP28@PITT18 SMU28@RICE30 WVA28@RUTG3 HI38@SDSU44 TUL26@SYR24 NDAME34@TENN29 HOUS24@TEX45 TTECH40@TCU28 NAVY14@TOL10 UCLA25@WASH22 ASU51@WSU26 BYU45@WYO14 PRIN7@YALE34 >N17 CINCI7@BAMA45 TA&M20@ARK16 GA10@AUBN33 OU6@BALL23 FORD7@BUCK44 STAN27@CAL25 SCAR15@CLEM24 HOLY35@COLG6 KSU3@COLO64 BROWN17@COLUM0 PENN15@CORN21 NCAR24@DUKE22 YALE34@HARV19 WYO17@HI38 ILL24@IND10 PURD9@IOWA38 OKSU25@ISU17 MO31@KAS21 EMICH24@KENTS25 FLA47@KY15 LHIGH35@LAFAY14 UNLV20@LBSU29 CSU30@LTECH31 FSU35,MEMPH3 BOST12@MIAMI42 MINN18@MICH35 LSU22@MSSU34 SDSU40@NMEX34 FULL9@NMSU43 MSU29@NW22 PSU24@NDAME21 OR6@ORSU3 DART23@PRIN6 BAYL17@RICE16 FRES7@SJSU42 NIL20@SWLA24 RUTG22@TEMP29 MISS13@TENN22 TEX38@TCU10 USAF14@UTEP13 SMU7@TTECH62 USC45@UCLA42 BYU42@UTAH22 PAC45@USU51 ARMY42@VAND38 MD35@VA30 GTECH42@WAKE7 WASH55@WSU10 SYR31@WVA7 MIOH17@WMICH31 OSU35@WIS10 >N22 WVA10@SCAR29 >N23 NEB10@OK45 >N24 ASU17@AZ21 TEX23@BAYL13 TEMP29@BOST10 USU10@BYU45 CSU30@HI27 NW23@ILL28 TUL13@LSU16 SYR7@MIAMI33 WIS9@MSU14 IOWA24@MINN31 MSSU9@MISS21 MICH16@OSU13 PITT17@PSU22 IND28@PURD14 UTEP31@SDSU58 NDAME10@USC6 ARK42@SMU29 KY28@TENN42 TCU10@TA&M56 WAKE56@VAND28 VA13@VTECH38 >D1 HOUS62,ASU45 BAMA16,AUBN7 FLA30@FSU45 GTECH40@GA23 BYU28@HI59 MIAMI30@SDSU28 TA&M27@TEX28 TENN49@VAND20 >D8 NAVY20@ARMY30 SJSU48,CMICH24 >D15 LTECH34,MD34 >D25 SYR28,AZ0 >D27 USAF23,OSU11 >D28 FSU24,PSU17 NCSU31,SMISS27 >D29 TA&M65,BYU14 AUBN27,IND23 CSU32,OR31 >D31 MSU17,USC16 CAL17,WYO15 >J1 LOUVL34,BAMA7 CLEM30,ILL0 WASH46,IOWA34 MICH35,MISS3 GTECH45,NEB21 COLO10,NDAME9 MIAMI46,TEX3 TENN23,VA22 * End of file "games.dat" homer.dat0000444000175000017500000007241005407367752011025 0ustar dondon* File "homer.dat" from the Stanford GraphBase (C) 1993 Stanford University * $\rm I\Lambda IA\Delta O\Sigma$, by Homer * This file may be freely copied but please do not change it in any way! * (Checksum parameters 670,795252274) AA Aethra, Trojan lady in waiting AB Abarbarea, Trojan fountain nymph AC Achilles, angry warrior, swift-footed chief of Myrmidons from Phthia AD Automedon, charioteer of AC AE Aeneas, leader of Dardanians AF Aphrodite (Venus), daughter of ZE and DN, roots for Trojans AG Agamemnon, king of Argos and Mycenae, leader of Greek forces AH Andromache, wife of HT AI Anchises, father of AE AJ Great Ajax, king of Salamis AL Antilochus, son of NE AM Artemis (Cynthia/Diana), daughter of ZE and LE, roots for Trojans AN Antenor, aged councilor to PR AO Agenor, heir of AN, assists AE AP Apollo, son of ZE and LE, roots for Trojans AR Ares (Mars), son of ZE, roots for Trojans AS Atreus, high king, father of AG and ME AT Athene (Minerva), daughter of ZE, favors Greeks AU Augeias, king in Elis, stables cleaned by HR AX Little Ajax, king of Locris, handiest with a spear AZ Aegaeon, hundred-armed giant, brother of CR BL Bellerophon, king of Lycia after killing CM, 2F, 2G BO Boreas, the north wind BR Briseis, prize in AC harem CA Calchas, wise prophet CH Chryses, priest of AP CI Charis, wife of HP CL Clymene, Trojan lady in waiting, cow-eyed CM Chimera, monster with head of lioness and tail of snake CN Chiron, centaur, instructor of 97 and AC CR Cronus (Saturn), father of HD, PO, ZE CS Chryseis, daughter of CH CT Castor, brother of HL, tamer of horses DE Death, twin brother of SL DI Diomedes, king of middle Argos, Tiryns, and Aegina DM Demeter (Ceres), goddess with beautiful hair, mother of PP DN Dione, consort of ZE DP Deiphobus, son of PR and HC DT Dionysus (Bacchus), god of wine EA Euryalus, lieutenant of DI EB Eurybates, herald of AG EE Eeriboea, stepmother of EF and OT EF Ephialtes, giant, brother of OT EM Eurymedon, Greek charioteer EN Enyo (Bellona), goddess of war EO Eos (Aurora), rosy-fingered and saffron-robed goddess of dawn EP Eurypylus, leader of forty ships from Thessaly ER Erinnyes (Furies), goddesses of vengeance EU Eurus, the east wind FD False Dream, messenger of ZE FY Phylus, son of AU, horseman favored by ZE GL Glaucus, comrade and squire of SA, grandson of BL GR Graces, handmaidens of AF GS Greek soldiers, collectively HA Hours (Horae), goddesses of the seasons HB Hebe, daughter of ZE and HE, goddess of youth HC Hecuba, wife of PR, queen of Troy HD Hades (Pluto), king of the underworld HE Hera (Juno), wife of ZE, favors Greeks HL Helen, wife of ME, brought to Troy by PS HM Hermes (Mercury), son of ZE, slightly favors the Greeks HN Helenus, son of PR, soothsayer HO Homer, the poet HP Hephaestus (Vulcan), crippled son of ZE and HE, favors Greeks HR Heracles (Hercules), heroic strong man HT Hector, eldest son of PR and HC, brilliant commander of Trojan army IA Idaeus, Trojan herald ID Idomeneus, king of Crete IR Iris, golden-winged Olympian messenger LA Laodice, loveliest daughter of PR and HC LE Leto (Latona), mother of AM and AP LT Leitus, leader of Boeotians LY Lycaon, brother of PS MC Ate, goddess of mischief, eldest daughter of ZE ME Menelaus, brother of AG, king of Sparta MG Meges, son of FY, flotilla leader from western islands MO Moira, personification of Fate MR Meriones, comrade and squire of ID MT Menestheus, Athenian leader, renowned chariot-fighter MU Muses, nine sisters who like to sing NE Nestor, venerable king of Pylus and Dorium NI Night, goddess of nighttime NO Notus, the south wind NR Nereids, sisters of TH OC Oceanus, father of all streams OD Odysseus (Ulysses), crafty king of Ithaca OG Olympian gods, collectively OT Otus, giant, brother of EF PA Patroclus, righthand man of AC PB Peneleos, leader of Boeotians PC Pelops, charioteer god PD Polydamus, Trojan prince, son of 0N PE Peleus, father of AC PH Podarge, a harpy (snatcher) PL Polites, son of PR PN Pandarus, son of LY, archer who breaks truce PO Poseidon (Neptune), king of the sea, favors Greeks PP Persephone (Proserpina), wife of HD, queen of nether world PR Priam, king of Troy, son of 1K PS Paris (Alexander), son of PR and friend of AF PT Prothoenor, Boeotian chief PU Polydeuces (Pollux), twin brother of CT, boxer PX Phoenix, king of Dolopians, tutor and foster father of AC RA Axius, god of river in Macedonia RH Rhea, consort of CR, mother of ZE, HE, PO, HD RO Robots, golden handmaidens fabricated by HP RU Rumor, goddess and servant of ZE SA Sarpedon, son of ZE and 2E, leader of the Lycians SE Semele, mother of DT SF Strife, twin of AR SI Simoeis, river god, tributary of XA SL Sleep, twin brother of DE SP Spercheius, tireless river god ST Sthenelus, lieutenant of DI TA Talthybius, herald of AG TE Telamon, father of AJ and TU TH Thetis, sea nymph, mother of AC TI Themis, fair-cheeked goddess of law TL Tlepolemus, son of HR and 9J, king of Rhodes TM Thrasymedes, son of NE TR Thersites, ugliest man in Greek army TS Trojan soldiers, collectively TT Tethys, wife of OC TU Teucer, half brother of AJ TY Thyestes, sheep breeder, brother of AS WI Aeolus, lord of winds XA Xanthus, son of ZE, god of the river Scamander XB Xanthus and Balius, AC's divine horses, sired by ZF ZE Zeus (Jove/Jupiter), king of the gods ZF Zephyr, the west wind 01 Archelochus, son of AN, lieutenant of AE 02 Acamas, son of AN, lieutenant of AE 03 Adrestus, son of 05, co-leader of Adresteians 04 Amphius, son of 05, co-leader of Adresteians 05 Merops, king of Percote, soothsayer 06 Asius, leader of Hellespontian forces 07 Hippothous, twin brother of 08, co-leader of Pelasgians 08 Pylaeus, twin brother of 07, co-leader of Pelasgians 09 Peirous, chief of Thracians, killed by 9K 0A Euphemus, leader of Ciconian spearmen 0B Pyraechmes, leader of Paeonian archers 0C Pylaemenes, leader of Paphlagonian forces 0D Odius, co-leader of Halizonian forces 0E Epistrophus, co-leader of Halizonian forces 0F Chromius, co-leader of Mysian forces 0G Ennomus, augur and co-leader of Mysian forces 0H Phorcys, co-leader of Phrygian forces 0I Ascanius, godlike co-leader of Phrygian forces 0J Mesthles, brother of 0K, co-leader of Maeonian forces 0K Antiphus, brother of 0J, co-leader of Maeonian forces 0L Nastes, brother of 0M, co-leader of Carian forces 0M Amphimachus, vain brother of 0L, co-leader of Carians 0N Panthous, Trojan elder, formerly priest at Delphi 0O Thymoetes, Trojan elder 0P Lampus, brother of PR 0Q Clytius, brother of PR 0R Hicetaon, brother of PR 0S Ucalegon, Trojan elder 0T Otreus, king of Phrygia 0U Mygdon, king of Phrygia 0V Echepolus, killed by AL 0W Simoeisius, killed by AJ 0X Democoon, bastard son of PR, killed by OD 0Y Phegeus, son of 10, killed by DI 0Z Idaeus, brother of 0Y, saved by HP 10 Dares, noble priest of HP in Troy 11 Phaestus, son of Maeonian nobleman, speared by ID 12 Scamandrius, Trojan archer trained by AM, speared by ME 13 Phereclus, Trojan shipbuilder, killed by MR 14 Theano, wife of AN, priestess of AT 15 Pedaeus, son of AN but not 14, killed by MG 16 Hypsenor, son of Trojan priest, killed by EP 17 Astynous, Trojan killed by DI 18 Hypeiron, Trojan killed by DI 19 Abas, brother of 1A, killed by DI 1A Polyeidus, son of Trojan soothsayer, killed by DI 1B Xanthus, brother of 1C, killed by DI 1C Thoon, brother of 1B, killed by DI 1D Echemmon, son of PR, killed by DI 1E Chromius, son of PR, killed by DI 1F Tros, ancient king of Troy 1G Ganymede, son of 1F, made cupbearer to OG by ZE 1H Deicoon, friend of AE, killed by AG 1I Mydon, 0C's driver, killed by AL 1J Amphius, fighter from Paesus, killed by AJ 1K Laomedon, king of Troy, father of PR, killed by HR 1L Hesione, daughter of 1K, rescued by HR 1M Coeranus, Lycian speared by OD 1N Alastor, Lycian speared by OD 1O Chromius, Lycian speared by OD 1P Alcandrus, Lycian speared by OD 1Q Halius, Lycian speared by OD 1R Noemon, Lycian speared by OD 1S Prytanis, Lycian speared by OD 1T Pelagon, attendant of SA 1U Acamas, Thracian commander, killed by AJ 1V Axylus, popular nobleman in suburban Troy, killed by DI 1W Calysius, charioteer of 1V, killed by DI 1X Dresus, Trojan killed by EA 1Y Opheltius, Trojan killed by EA 1Z Aesepus, son of 21 and AB, killed by EA 20 Pedasus, twin brother of 1Z, killed by EA 21 Bucolion, illegitimate eldest son of 1K 22 Astyalus, Trojan killed by 94 23 Pidytes, warrior from Percote, killed by OD 24 Aretaon, Trojan soldier killed by TU 25 Ablerus, Trojan soldier killed by AL 26 Elatus, soldier from Pedasus, killed by AG 27 Phylacus, Trojan soldier killed by LT 28 Melanthius, Trojan soldier killed by EP 29 Anteia, wife of 82 2A Iobates, ancient king of Lycia, father of 29 and 2B 2B Philonoe, wife of BL 2C Isander, son of BL and 2B 2D Hippolochus, son of BL and 2B, father of GL 2E Laodameia, daughter of BL and 2B 2F Solymi, Lycian tribe 2G Amazons, warlike community of women 2H Trojan noblewomen, assembled by HC 2I Eetion, king of Cilicia, father of AH 2J Scamandrius, infant son of HT and AH, nicknamed Astyanax 2K Eniopeus, HT's charioteer, killed by DI 2L Archeptolemus, 2K's replacement 2M Agelaus, Trojan warrior killed by DI 2N Orsilochus, Trojan warrior shot by TU 2O Ormenus, Trojan warrior shot by TU 2P Ophelestes, Trojan warrior shot by TU 2Q Daetor, Trojan warrior shot by TU 2R Chromius, Trojan warrior shot by TU 2S Lycophontes, Trojan warrior shot by TU 2T Amopaon, Trojan warrior shot by TU 2U Melanippus, Trojan warrior shot by TU 2V Gorgythion, son of PR, shot by TU 2W Cebriones, half-brother of HT, 2L's replacement 2X Dolon, rich, swift-footed, ugly Trojan 2Y Rhesus, Thracian king, murdured in sleep by DI 2Z Hippocoon, cousin of 2Y 30 Mestor, deceased son of PR 31 Polybus, son of AN 32 Bienor, Trojan commander killed by AG 33 Oileus, charioteer of 32, killed by AG 34 Antiphus, son of PR, killed by AG 35 Isus, illegitimate son of PR, killed by AG 36 Antimachus, Trojan councillor 37 Peisander, son of 36, killed by AG 38 Hippolochus, son of 36, killed by AG 39 Iphidamus, son of AN and 14, killed by AG 3A Cisses, father of 14, guardian of 39 3B Coon, eldest son of AN, killed by AG after wounding him 3C Thymbraeus, Trojan warrior killed by DI 3D Molion, charioteer of 3C, killed by OD 3E Hippodamus, Trojan warrior killed by OD 3F Hypeirochus, Trojan warrior killed by OD 3G Agastrophus, Trojan warrior killed by DI 3H Deiopites, Trojan warrior killed by OD 3I Thoon, Trojan warrior killed by OD 3J Ennomus, Trojan warrior killed by OD 3K Chersidamas, Trojan warrior killed by OD 3L Charops, Trojan warrior, brother of 3M, killed by OD 3M Socus, wealthy Trojan warrior, killed by OD 3N Doryclus, illegitmate son of PR, killed by AJ 3O Pandocus, Trojan warrior killed by AJ 3P Lysander, Trojan warrior killed by AJ 3Q Pyrasus, Trojan warrior killed by AJ 3R Pylartes, Trojan warrior killed by AJ 3S Apisaon, Trojan warrior killed by EP 3T Alcathous, brother-in-law of AE, killed by ID 3U Troilus, deceased son of PR 3V Asteropaeus, leader of the Paeonians 3W Adamas, son of 06, killed by MR 3X Iamenus, fighter with 06 3Y Orestes, fighter with 06, killed by 93 3Z Thoon, fighter with 06, killed by AL 40 Oenomaus, fighter with 06 41 Damasus, Trojan warrior killed by 94 42 Pylon, Trojan warrior killed by 94 43 Ormenus, Trojan warrior killed by 94 44 Antiphates, Trojan warrior killed by 93 45 Menon, Trojan warrior killed by 93 46 Iamenus, Trojan warrior killed by 93 47 Epicles, comrade of SA, killed by AJ 48 Imbrius, husband of 49, killed by TU 49 Medesicaste, illegitimate daughter of PR 4A Cassandra, prophetess, daughter of PR 4B Othryoneus, suitor of 4A, killed by ID 4C Hippodameia, talented eldest daughter of AI 4F Peisander, Trojan warrior killed by ME 4G Harpalion, son of 0C, killed by MR 4H Phalces, Trojan warrior, killed by AL 4I Orthaeus, Trojan warrior 4J Polyphetes, Trojan warrior 4K Palmys, Trojan warrior 4L Ascanius, Trojan warrior 4M Morys, Trojan warrior killed by MR 4N Satnius, Trojan warrior killed by AX 4O Ilioneus, Trojan warrior killed by PB 4P Hyrtius, Trojan warrior killed by AJ 4Q Mermerus, Trojan warrior killed by AL 4R Hippotion, Trojan warrior killed by MR 4S Prothoon, Trojan warrior killed by TU 4T Periphetes, Trojan warrior killed by TU 4U Hyperenor, son of 0N, Trojan warrior killed by ME 4V Caletor, son of 0Q, killed by AJ 4W Cleitus, Trojan warrior killed by TU 4X Astynous, charioteer for PD 4Y Laodamas, Trojan warrior killed by AJ 4Z Croesmus, Trojan warrior killed by MG A0 Dolops, son of 0P, killed by ME B0 Melanippus, neighber of PR, killed by AL C0 Areilycus, Trojan warrior killed by PA D0 Thoas, Trojan warrior killed by ME E0 Amphiclus, Trojan warrior killed by MG F0 Atymnius, friend of SA, Trojan warrior killed by AL G0 Maris, friend of SA, Trojan warrior killed by TM H0 Amisodarus, father of F0 and G0 I0 Cleobolus, Trojan warrior killed by AX J0 Lycon, Trojan warrior killed by PB K0 Erymas, Trojan warrior killed by ID L0 Pronous, Trojan warrior killed by PA M0 Thestor, Trojan warrior killed by PA N0 Euryalus, Trojan warrior killed by PA O0 Erymas, Trojan warrior killed by PA P0 Amphoterus, Trojan warrior killed by PA Q0 Epaltes, Trojan warrior killed by PA R0 Tlepolemus, Trojan warrior killed by PA S0 Echius, Trojan warrior killed by PA T0 Pyris, Trojan warrior killed by PA U0 Ipheus, Trojan warrior killed by PA V0 Evippus, Trojan warrior killed by PA W0 Polymelus, Trojan warrior killed by PA X0 Thrasymelus, charioteer of SA, killed by PA Y0 Sthenelaus, Trojan warrior killed by PA Z0 Laogonus, Trojan warrior killed by MR A1 Adrestus, Trojan warrior killed by PA B1 Autonous, Trojan warrior killed by PA C1 Echeclus, Trojan warrior killed by PA D1 Perimus, Trojan warrior killed by PA E1 Epistor, Trojan warrior killed by PA F1 Melanippus, Trojan warrior killed by PA G1 Elasus, Trojan warrior killed by PA H1 Mulius, Trojan warrior killed by PA I1 Pylartes, Trojan warrior killed by PA J1 Euphorbus, son of 0N, killed by ME after wounding PA K1 Medon, Trojan leader L1 Thersilochus, Trojan leader M1 Deisenor, Trojan leader N1 Apisaon, Trojan warrior killed by 7R O1 Aretus, son of PR, killed by AD P1 Podes, son of 2I, killed by ME Q1 Ilus, son of 1F, ancient king of Troy R1 Assaracus, son of 1F, grandfather of AE S1 Erichtonius, richest man alive, father of 1F T1 Iphition, Trojan warrior killed by AC U1 Demoleon, son of AN, killed by AC V1 Hippodamus, driver of U1, killed by AC W1 Polydorus, youngest and favorite son of PR, killed by AC X1 Dryops, Trojan warrior killed by AC Y1 Demuchus, Trojan warrior killed by AC Z1 Loagonus, Trojan warrior killed by AC A2 Dardanus, brother of Z1, killed by AC B2 Tros, son of 1N, Trojan warrior killed by AC C2 Mulius, Trojan warrior killed by AC D2 Echeclus, son of AO, Trojan warrior killed by AC E2 Deucalion, Trojan warrior killed by AC F2 Rhigmus, Trojan warrior killed by AC G2 Areithous, driver of F2, killed by AC H2 Eetion, guest-friend of PR I2 Laothoe, concubine of PR J2 Pelegon, father of 3V, son of RA and K2 K2 Periboea, mother of J2 L2 Thersilochus, charioteer from Paeonia killed by AC M2 Mydon, charioteer from Paeonia killed by AC N2 Astypylus, charioteer from Paeonia killed by AC O2 Mnesus, charioteer from Paeonia killed by AC P2 Thrasius, charioteer from Paeonia killed by AC Q2 Aenius, charioteer from Paeonia killed by AC R2 Ophelestes, charioteer from Paeonia killed by AC S2 Altes, king of the Leleges, father of I2 T2 Agathon, son of PR U2 Pammon, son of PR V2 Antiphonus, son of PR W2 Hippothous, son of PR X2 Dius, arrogant son of PR 51 Melas, brother of 82 52 Agrius, brother of 82 53 Niobe, queen of Thebes 54 Oedipus, king of Thebes 55 Mecisteus, father of EA 56 Epeius, huge Greek warrior, champion boxer 57 Actor, brother of AU 58 Laodocus, comrade of AL 59 Coeranus, charioteer of MR, killed by HT 5A Moliones, twin brothers, purported sons of 57 5B Leiocritus, Greek warrior killed by AE 5C Bathycles, wealthy Myrmidon, killed by GL 5D Epeigeus, Myrmidon leader killed by HT 5E Alcimedon, Myrmidon leader 5F Peisander, Myrmidon leader 5G Eileithyia, daughter of HE, goddess of childbirth 5H Phylas, father of 5J 5I Echecles, married 5J after 5K was born 5J Polymele, singer and dancer 5K Eudorus, Myrmidon leader, son of HM and 5J 5L Borus, putative father of 5O 5M Polydorus, javelin-thrower once beaten by NE 5N Polydora, sister of AC 5O Menestheus, Myrmidon leader, son of 5N and SP 5P Periphetes, son of 5Q 5Q Copreus, herald of 7U 5R Otus, comrade of MG, killed by PD 5S Schedius, Greek warrior killed by HT 5T Lycophron, squire of AJ, killed by HT 5U Deiochus, Greek warrior killed by PS 5V Iasus, Athenian leader killed by AE 5W Archesilaus, friend of MT, killed by HT 5X Phyleus, javelin-thrower once beaten by NE 5Y Iphiclus, runner once beaten by NE 5Z Promachus, Greek warrior killed by 02 60 Areilycus, Greek warrior killed by PD 61 Ancaeus, wrestler once beaten by NE 62 Alcmene, mother of HR 63 Clytomedes, boxer once beaten by NE 64 Europa, mother of 65 and 66 65 Rhadamanthus, brother of 66 66 Minos, ancient king of Crete 67 Perseus, Greek hero 68 Danae, mother of 67, had beautiful ankles 69 Dia, mother of 6A 6A Peirithous, son of ZE and 69, king of the Lapiths 6B Noemon, comrade of AL 6C Echepolus, subject of AG who avoids the war 6D Hypsipyle, wife of Jason 6E Janos, leader of the Argonauts 6F Deipyle, mother of DI 6G Dracius, lieutenant of MG 6H Amphion, lieutenant of MG 6I Bias, lieutenant of MT 6J Pheidas, lieutenant of MT 6K Polyidus, seer from Corinth 6L Euchenor, son of 6K, killed by PS 6M Mecisteus, companion of AL, killed by PD 6N Hypsenor, Greek warrior slain by DP 6O Stichius, lieutenant of MT, killed by HT 6P Alcmaon, Greek warrior killed by SA 6Q Pandion, bow-bearer of TU 6R Thootes, Greek herald 6S Menoetius, father of PA 6T Moliones, twin sons of PO 6U Mulius, husband of 6V, killed by NE 6V Agamede, eldest daughter of AU 6W Neleus, king of Pylos, father of NE 6X Itymoneus, Elean cattleman killed by NE 6Y Hecamede, female slave presented by AC to NE 6Z Hipponous, Greek warrior killed by HT 70 Orus, Greek warrior killed by HT 71 Aesymnus, Greek warrior killed by HT 72 Agelaus, Greek warrior killed by HT 73 Opheltius, Greek warrior killed by HT 74 Dolops, Greek warrior killed by HT 75 Opites, Greek warrior killed by HT 76 Autonous, Greek warrior killed by HT 77 Asaeus, Greek warrior killed by HT 78 Cinyras, king of Cyprus 79 Tithonus, son of 1K, mortal consort of EO 7A Molus, father of MR 7B Amphidamas, Argonaut from Cythera 7C Autolycus, sly grandfather of OD 7D Iphis, slave girl captured by Achilles on Scyros 7E Diomede, slave girl captured by Achilles on Lesbos 7F Marpessa, had beautiful ankles 7G Idas, strongest man of his day 7H Cleopatra, wife of 7J, daughter of 7G and 7F 7I Althea, wife of 82, curses 7J 7J Meleager, son of 82 and 7I 7K Clytia, beautiful slave girl of 7L 7L Amyntor, father of PX 7M Odius, Greek herald 7N Iphianassa, daughter of AG 7O Laodice, daughter of AG 7P Chrysothemis, daughter of AG 7Q Orestes, son of AG, student deferred from draft 7R Lycomedes, Greek captain of 100 spearmen 7S Deipyrus, Greek captain of 100 spearmen, killed by HN 7T Aphareus, Greek captain of 100 spearmen, killed by AE 7U Eurystheus, king of Mycenae, made HR labor 7V Echius, father of 6M 7W Ereuthalion, favorite squire of 7X 7X Lycurgus, Arcadian warrior, killer of 7Y 7Y Areithous, king of Arne in Thrace 7Z Iphinous, Greek warrior slain by GL 80 Eioneus, Greek from Magnesia, slain by HT 81 Menesthius, son of 7Y, killed by PS 82 Oeneus, king of Calydon 83 Proetus, king of Tiryns, overlord of BL 84 Glaucus, son of 85, father of BL 85 Sisyphus, crafty son of WI 86 Clysonomus, boy killed in quarrel by young PA 87 Lycurgus, king of the Edonians 88 Periphas, huge fighter, bravest of the Aetolians 89 Oresbius, Boeotian landowner, slain by HT 8A Helenus, Greek fighter, slain by HT 8B Trechus, Aetolian fighter, slain by HT 8C Orestes, Greek charioteer, slain by HT 8D Teuthras, Greek from Magnesia, slain by HT 8E Anchialus, veteran Greek fighter slain by HT 8F Menesthes, veteran Greek fighter dispatched by HT 8G Orsilochus, twin brother of 8H, lopped down by AE 8H Crethon, twin brother of 8G, lopped down by AE 8I Euneus, king of Lemnos, son of 6E and 6D 8J Alcimus, friend of AC 8K Melanippus, Greek leader 8L Deipylus, companion of ST 8M Leucus, companion of OD 8N Popyphontes, Theban warrior, killed by 8R 8O Maeon, Theban warrior, spared by 8R 8P Eteocles, brother of 8Q 8Q Polyneices, king of Thebes 8R Tydeus, king of Calydon, son of 82, father of DI 8T Bias, Pylian commander 8U Haemon, Pylian commander 8V Chromius, Pylian commander 8W Alastor, Pylian commander 8X Pelagon, Pylian commander 8Y Daedalus, Athenian architect 8Z Ariadne, daughter of 66 90 Eurynome, daughter of OC 91 Prothous, commander of Magnesian forces 92 Guneus, king of Cyphus, leader of Thessailian contingent 93 Leonteus, co-leader with 94 of Thessalian contingent 94 Polypoetes, son of 6A, co-leader of Thessalian contingent 95 Machaon, physician and co-leader of Thessalian contingent 96 Podaleirius, physician and co-leader of Thessalian contingent 97 Asclepius, famous physician, father of 95 and 96 98 Medon, step-brother of AX, replaced 99 as leader of Thessalian contingent 99 Philoctetes, famous archer bitten by a snake 9A Eumelus, leader of Thessalian contingent 9B Podarces, brother of 9C, leader of Thessalian contingent 9C Protesilaus, the first Greek to land and the first casualty 9D Epistrophus, brother of 9E, slain by AC 9E Mynes, spearman slain by AC 9F Antiphus, grandson of HR, leader of troops from Dodecanese islands 9G Pheidippus, brother of 9F, leader of troops from Dodecanese islands 9H Nireus, Symian leader, handsomest Greek excluding AC 9I Licymnius, uncle of HR, murdered by TL 9J Astyocheia, captured at Ephyra by HR 9K Thoas, leader of Aetolians 9L Polyxeinus, leader of an Epeian flotilla 9M Diores, leader of an Epeian flotilla, killed by 09 9N Thalpius, leader of an Epeian flotilla 9O Amphimachus, son of Cteatus, leader of an Epeian flotilla 9P Agapenor, king of Arcadians 9Q Thamyris, Thracian bard 9R Erechtheus, ancient ruler of Athens 9S Elephenor, leader of the long-haired Abantes 9T Epistrophus, son of Iphitus, Phocian leader 9U Schedius, son of Iphitus, Phocian leader 9V Astyoche, mother of 9X and 9W 9W Ialmenus, son of AR and 9V, leader of Minyans 9X Ascalaphus, twin brother of 9W 9Y Clonius, Boeotian leader slain by AO 9Z Arcesilaus, Boeotian leader 1:CH,AG,ME,GS;AP,CH;HE,AC;AC,AG,CA;HE,AT;AT,AC;AT,OG;NE,AG,AC;CS,OD &:TA,EB,PA,AC,BR;TH,PO;AC,2I;AC,TH,PE;TH,AZ;AZ,ZE;CS,OD,CH;EO;ZE,OG &:TH,ZE;ZE,HE;HE,HP;HP,OG;HP,ZE 2:ZE,FD;FD,AG;EO,OG;AG,NE;RU,GS;HP,ZE;ZE,HM;HM,PC;PC,AS;AS,TY;TY,AG &:AG,GS;NO;EU;ZF;HE,AT;AT,OD;OD,EB;OD,GS;TR,OD,AC;TR,GS;OD,TR;OD,AT,GS &:CA,GS;NE,GS,AG;PS,HL;AG,NE,ID,AJ,AX,DI,OD,ME;AT,GS;MU,HO;PB,LT,9Z,PT,9Y &:9W,9X,AR,9V;9U,9T;AX;9S;9R,AT;AJ;DI,ST,EA;AG;ME;NE;MU,9Q;9P;9O,9N,9M,9L &:9K;ID;MG,OD;FY,AU;AU,HR;HR,9J;TL,9I;9H;9G,9F;AC,BR;AC,9E,9D;9C,9B;99 &:9B,9A,98,96,95,EP,94,93,92;97;91;IR,PR;IR,HT;AE,01,02;AI,AF;PN,AP;03,04,05 &:06;07,08;02,09,0A;0B;RA;0C;0D,0E;0F,0G;0H,0I;0J,0K;0L,0M;0M,AC;SA,GL 3:PS,ME;HT,PS;PS,HL;AG,GS;HT,GS,TS;ME,GS,TS;AG,TA;IR,HL &:HL,AA,CL,PR,0N,0O,0P,0Q,0R,0S,AN;PR,0T,0U;AG;AN,OD,ME;AJ;ID,ME,HL;CT,PU &:IA,PR;ZE;PR,AN,AG;HT,OD;PS,LY;PS,ME,AF;AF,HL,AA,CL;AF,HL,PS 4:ZE,OG;OG,HB;ZE,HE,AT;AT,PN,ME;AG,ME;AG,TA;TA,95;97,CN;AG,EM;AG,GS;AG,ID,MR &:AG,AJ,AX;AG,NE,8X,8W,8V,8U,8T;AG,MT,OD;AG,DI,ST;8R,8Q;8R,8P,AT,8O,8N;AT,GS &:AR,TS;SF,GS,TS;AL,0V,9S;9S,AO;AJ,0W;34,AJ,8M;OD,0X;HT;AP,TS;AT,GS;09,9M;9K,09 5:AT,DI;0Y,0Z,10;0Y,0Z,DI;0Z,HP;AT,AR;AG,0D;11,ID;ME,12;12,AM;AT,13;13,MR &:14,15,AN;15,MG;EP,16;DI,PN;DI,ST;DI,AT;DI,17;DI,18;DI,19,1A;DI,1B,1C &:DI,1D,1E;AE,PN;LY,PN;ST,DI;ZE,1F;ZE,1G;AI,AE;DI,PN,AE;AF,AI;AF,AE;ST,8L &:DI,AF;AP,AE;AF,GR;IR,AF;AF,AR;AF,DN;EF,OT,AR;EE,HM;HM,AR;HR,HE;HR,HD &:HD,AP;HR,OG;AT,HE,ZE;ZE,AF;DI,AP,AE;AM,LE,AE;AP,AR,SF;SA,HT;HT,TS;TS,GS &:TS,AE;DI,OD,AJ,AX;AG,1H;AE,8G,8H;ME,AR;ME,AL,AE;ME,AL,8G,8H;ME,AL,0C,1I &:HT,TS,AR,EN;HT,8E,8F;AJ,1J;AJ,TS;TL,SA;HR,1L;HR,1K;ZE,SA;OD,AT;OD,1M &:OD,1N;OD,1O;OD,1P;OD,1Q;OD,1R;OD,1S;HT,OD;SA,HT;SA,1T;HT,GS;HT,8D;HT,8C &:HT,8B;HT,8A;HT,89;HE,AT;HE,HB;HE,AT,HA;HE,ZE;AF,AP;HE,DI,GS;AT,DI,ST &:AT,8R;AT,DI,AR;AR,88;AR,ZE,AP;HB,AR 6:GS,TS;AJ,1U;DI,1V,1W;EA,1X;EA,1Y;EA,1Z,20;1Z,20,AB,21;94,22;OD,23;TU,24 &:AL,25;AG,26;LT,27;EP,28;ME,03,AG;NE,GS;HN,HT,AE;HT,TS;GL,DI;87,DT;DT,TH &:OG,87;85,WI;85,84;84,BL;ZE,83,BL;29,83,BL;2A,BL;EO,2A;BL,CM;BL,2F;BL,2G &:2A,BL,2B;2B,BL,2C,2D,2E;ZE,2E;2E,SA;AR,2C,2F;AM,2E;2D,GL;82,BL;ZE,GL &:HT,HC,LA;HC,2H,14,AT;HT,PS,HL;HT,AH,2I;HT,AH,2J;AC,2I;AJ,AX,ID,AG,ME,DI;PS,HT 7:PS,81;HT,80;GL,7Z;AT,AP,HN;HT,TS;AG,GS;HT,TS,GS;ME,GS;AG,ME;NE,GS;NE,PE &:AR,7Y;7X,7Y;7X,7W;NE,7W;AG,DI,AJ,AX,ID,MR,EP,9K,OD;AJ,GS;AJ,HT;AP,HT &:AJ,HT,TA,IA;AG,AJ;AG,NE;AN,PS,PR;IA,AG,ME,DI;IA,PR;PO,ZE;EO;PO,AP,1K;6D,6E,8I 8:EO;ZE,OG;AT,ZE;GS,TS;ID,AG,AJ,AX;NE,PS;NE,HT;DI,OD;DI,NE,EM,ST;DI,HT,2K &:HT,2L;HT,TS;HE,PO;HE,AG;AG,GS;DI,2M;AG,ME,AJ,AZ,ID,MR,EP,TU;TU,2N;TU,2O;TU,2P &:TU,2Q;TU,2R;TU,2S;TU,2T;TU,2U;AG,TU;TE,TU;TU,2V;HT,TU;HT,2W;AJ,TU,6M,8W &:HT,GS;HE,AT;7U,HR,HD;HA,AT,HE;ZE,IR;IR,HE,AT;ZE,PO;ZE,AT,HE;HT,TS;PO,AP,1K 9:AG,DI,NE;TM,9X,9W,MR,7T,7S,7R;NE,AG;7Q;7P,7O,7N;HD;PX,AJ,OD,7M,EB,AC,PA &:AD,AD,PA;PE,AC;BR;TH;PX,7L,7K;7L,HD,PP;PX,PE;82,AM;7I,7J;7I,HD,PP,ER &:7J,7H;7G,AP,7F;82,7J;AC,7E;PA,7D;AG,OD,DI 10:AG,ME;AG,NE;NE,OD,DI,ME,ID,AJ,MR,TM,AX,AG;7L,7C;7C,7B;7B,7A;AT,OD,DI &:HT,2X;2X,OD,DI;DI,2Y;AP,2Z 11:EO,79;SF,GS;AG,78;AG,HE,AT;HT,PD,AE,31,AO,02;HT,TS;TS,GS;SF;OG;AF,AP &:AG,32;AG,33;AG,34,35;AC,34,35;AC,PR;36,PS;AG,37,38;ZE,HT,IR;MU;39,3A &:AG,39;AG,3B;3A,3B,HD;AG,GS;HT,TS;HT,77;HT,76;HT,75;HT,74;HT,73;HT,72 &:HT,71;HT,70;HT,6Z;DI,OD,3C,3D;DI,03,04;OD,3E;OD,3F;DI,3G;DI,HT;AP,HT &:PS,DI;DI,ST;OD,TS;OD,3H;OD,3I;OD,3J;OD,3K;OD,3L;AT,OD;OD,3M;ME,AJ,OD &:AJ,3N;AJ,3O;AJ,3P;AJ,3Q;AJ,3R;PS,95;ID,NE;NE,95;HT,2W;EP,AJ;EP,3S;PS,EP &:AC,PA;EM,NE,95;6Y,NE,95;6Y,AC;NE,95,PA;NE,6X;NE,6W,HR;AT,NE;NE,6U &:6U,6V;NE,6T;PO,5A;5A,57;NE,OD,6S,AC,PA,PE;PA,EP;CN,PA;96 12:PA,EP;PO,AP,ZE;HT,GS;PD,HT,2W;PS,3T,AO;HN,DP,06;AE,01,02;SA,GL,3V &:06,93,94,3W,3X,3Y,3Z,40;94,41;94,42;94,43;93,44;93,45;93,46;93,3Y;PD,HT &:HT,TS;GS,TS;GS,AJ,AX;ZE,SA;SA,GL;MT,6R;6R,AJ,AX;AJ,TU,6Q,MT;AJ,47;TU,GL &:SA,6P;AJ,TU,SA,ZE;SA,TS,MT,GS;ZE,HT,TS 13:ZE;PO,AJ,AX;PO,TU,LT,PB,9K,7S,MR,AL;GS,HT;DP,MR;TU,48;48,49;48,PR;HT,TU,9O &:AJ,HT;6O,MT;PO,ID;ID,MR;4B,4A,PR;4B,ID;06,ID,AL;DP,ID,6N;AL,6M,8W,6N;ID,3T,PO &:AI,4C,3T;DP,AE,PS,AO,ID,9X,7T,7S,MR,AL;ID,40;DP,9X;OG;DP,MR;DP,PL;AE,7T &:AL,3Z;3W,AL;3W,MR;HN,7S;ME,HN;HN,AO;ME,4F;4G,ME;MR,4G;4G,PS;0C,4G;PS,6L &:6K,6L;HT,AX,9B,MT;MT,6O,6J,6I;MG,6G,6H;9B,98;AJ,AX,TS;PD,HT &:HT,PS,PD,2W,4H,4I,4J,4K,4L;AJ,HT,TS,GS 14:NE,95;6Y;TM;NE,DI,OD,AG;51,52,82;82,8R;8R,6F;PO,AG;HP;AT;HE,AF;HE,RH,OC,TT &:HE,SL;HE,HR;ZE,OG;SL,NI;SL,GR;HE,ZE;ZE,69,6A;ZE,68,67;ZE,64,65;ZE,64,66 &:ZE,SE,DT;ZE,62,HR;ZE,DM,PP;ZE,LE,AP,AM;ZE,SL;SL,PO;PO,GS;DI,OD;HT,AJ &:PD,AE,AO,SA,GL,HT;AX,4N;PD,60;AJ,PD,01;02,5Z;PB,02;PB,4O;MU;AJ,4P &:AL,4H;AL,4Q;MR,4M;MR,4R;TU,4S;TU,4T;ME,4U;AX,TS 15:ZE,HE;PO,GS;TS,HT;HE,HR;ZE,HR;HE,OG;HE,TI;HE,AR,AT;HE,AP,IR;ZE,IR;IR,PO &:PO,ZE,HD,RH,CR;ER;ZE,AP;AP,HT;9K;AJ,ID,EU,MG,HT,AP,TS;HP,ZE;HT,6O;HT,5W &:AE,98;AE,5V;PD,6M;PL,7V;AO,9Y;5U,PS;NE;PA,EP;HT,AJ;AJ,4V;HT,5T;5T,AJ;TU,4W &:PD,4X;TU,HT;AJ,TU;HT,TS;AJ,GS;HT,5S;AJ,4Y;PD,5R;MG,PD,AP;MG,4Z;A0,MG;ME,A0 &:HT,B0;ME,AL;AL,B0;HT,AL;5Q,HR;5Q,7U;HT,5P;NE,GS;AT;AJ;GS,TS 16:PA,AC;ZE;MU;HT,AJ;CN,PE;PA,AD,XB;ZF,PH,XB;5L,PE,5N;SP,5N,5O;HM,5J,5K &:5K,5G;5I,5J,5H;5H,5K;5O,5K,5F,PX,5E,AC;TH;AC,ZE;PA,0B;PA,C0;ME,D0;MG,E0 &:AL,F0;TM,G0;H0,CM;AX,I0;PB,J0;MR,02;ID,K0;AJ,HT;PA,HT;PA,L0;PA,M0;PA,N0 &:PA,O0;PA,P0;PA,Q0;PA,R0;PA,S0;PA,T0;PA,U0;PA,V0;PA,W0;SA,PA;HE,ZE;PA,X0;AD &:SA,GL;GL,AP;GL,PD,AG,AE,HT;PA,AJ,AX;5D,PE,TH;5D,HT;PA,Y0;GL,5C;MR,Z0;AE,MR &:PA,MR;ZE,AP;AP,SA;SL,DE,SA;PA,XB;ZE,PA;PA,AD;PA,A1;PA,B1;PA,C1;PA,D1;PA,E1 &:PA,F1;PA,G1;PA,H1;PA,I1;PA,AP;AP,HT;HT,2W,PA;EU,NO;AP,PA;J1,PA;HT,PA 17:ME,4U;ME,J1;AP,HT;ME,AJ,PA;GL,HT;OG,PE,XB;PE,AC,XB &:HT,0J,GL,K1,L1,3V,M1,07,0H,0F,0G,AJ,ME;AX,ID,MR,AJ,TS;07,PA,AJ;HT,AJ,9U &:0H,07,AJ;AP,AE;AE,TS;AE,5B;7R,N1;3V,AJ;AL,TM;AC;GS,TS;AD,XB;ZE,XB;5E,AD &:HT,AE,0F,O1;AD,O1;HT,AD;AX,AD;ZE,AT;AT,ME;HT,P1;ME,P1;AP,HT;PD,PB;HT,LT &:ID,HT,59;MR,ID;AJ,MR,AX,ME;ME,AL;AL,58;ME,TM;ME,MR,AJ,AX;HT,AE,GS 18:AC,AL;TH,NR;TH,AC;HE,HR;HT,AE,AJ,AX,PA;HE,IR;IR,AC;AT,AC;PD,HT;AC,PA &:TH,HP,CI;TH,90,HP;HP,RO,TH;AG,AC,BR;AC,PA;AP,PA,HT;8Y,8Z 19:EO,OC;TH,AC,PA;AC,GS,DI,OD,AG;AG,3B;AM,BR;ZE,MO,ER;ZE,HE,MC;HE,7U,62 &:OD,AC,AG;OD,AL,TM,MG,9K,MR,7R,8K,BR;TA,AG;AG,GS,AC;BR,PA;AG,ME,NE,ID,PX,AC,PA &:ZE,AT;AT,AC;CN,PE;AD,8J,XB;AD,AC,XB;HE,XB;XB,ER 20:ZE,TI;TI,OG;HP,ZE;PO,ZE;HE,AT,PO,HM,HP;AR,AP,AM,LE,XA,AF;GS,TS,SF;AP,PO &:AT,AR;AM,HE;HM,LE;HP,XA;HD;AP,AE;AE,AC;ZE,AE;AT,AC;AE,AF;MO,AC;AT,TS &:HR,1L;AE,AC;AC,ZE,AT;OG,AE;S1,BO;1F,1G,Q1,R1;OG,1G;1K,79,0P,0Q,0R &:AE,AI;PO,HE;PO,AC;PO,AE;AC,GS;HT,TS;AP,HT;AC,T1;AC,U1,V1;AC,W1;AC,HT &:AP,HT;AT,AC;AC,X1;AC,Y1;AC,Z1,A2;AC,B2;AC,C2;AC,D2;AC,E2;AC,F2,G2 21:AC,TS;AC,LY,8I;H2,8I;I2,PR,W1;I2,PR,LY;XA,AC;3V,J2;J2,RA,K2;XA,3V;AC,3V &:OC,ZE;AC,L2,M2,N2,O2,P2,Q2,R2;XA,AP;PO,AT,AC;XA,SI;HE,HP;XA,HP;NO,ZF;BO &:HP,XA,HE;ZE;AR,AT;DI,AR;HE,AR;AR,AF;HE,AT;AT,AF,AR;PO,AP,1K;ZE,1K;AP,AM &:HE,AM;HM,LE;AM,ZE;PR,TS;AP,TS;AP,AO;AO,AC 22:TS;HT,CS;AP,AC;PR,HT;PR,I2,S2;HC,HT,PR;HT,AC;ZE,OG;AT,ZE;AP,HT;AT,AC &:AT,HT;HT,HD;GS,HT;AC,XB,HT;HC,PR;AH,HT,AF;2I,HT;2J,HT 23:AC,GS,TH;AC,PA,HT;AC,AG;86,PA;6S,PA;PE,PA,AC;EO,GS;AG,MR;AP,AF,HT;AC,IR &:IR,ZF,BO,NO,EU;BO,ZF,AC,PA;AC,AG,GS;PO,PE,XB;9A,DI,ME,AL,MR;6C,AG;NE,AL &:AP,DI;AT,DI;AT,9A;ID,AX,AC;ST,DI;AC,9A;AC,AL;AC,3V;AM,9A;TA,ME;ME,AL;ME,6B &:MR;AC,NE;NE,63;NE,61;NE,5Y;NE,5X,5M;NE,5A;56,EA;DI,EA;55,54;AJ,OD;8I,LY,PA &:AJ,OD,AL;AT,OD;AT,AJ;AG,MR;AG,TA 24:AC,HT;AP,HT;HE,AT,AF,PS;PS,HL;PO,HE,AT;AP,OG;MO;HE,AP;ZE,HE;AC,TH;ZE,IR &:AT,HE,TH,OG;ZE,TH;IR,PR;PR,HC;PR,HN,PS,T2,U2,V2,PL,DP,W2,X2;30,3U,HT;PR,IA &:PR,ZE;ZE,HM;HM,PR,IA;AC,AD,8J;PR,AC;AC,IA;AC,PA;53,AP,AM;AC,BR;HM,PR,IA,HT &:4A,TS,AH,HC,HT,PR,IA;AH,HT;HC,HT;HL,HT;EO * End of file "homer.dat" huck.dat0000444000175000017500000001017305407367760010642 0ustar dondon* File "huck.dat" from the Stanford GraphBase (C) 1993 Stanford University * Huckleberry Finn, by Mark Twain * This file may be freely copied but please do not change it in any way! * (Checksum parameters 118,822615693) AB Abner Shackleford, friend of PW AP Aunt Polly, aunt who raises TS AS Aunt Sally Phelps, sister of AP AT Ab Turner, undertaker's assistant BD Bud Grangerford, cousin of BK BE Betsy, slave of RG BG Bob Grangerford, eldest son of CG and RG BH Buck Harkness, incites lynch mob BI Bill, thief wants to shoot TU BK Buck Grangerford, youngest son of CG and RG BM Brother Marples, friend of AS and SP BN Ben Rucker, friend of PW BO Boggs, town drunk BP Brer Penrod, friend of AS and SP BR Ben Rogers, member of TS's gang BS Baldy Shepherdson, kills BU in feud BT Bessie Thatcher, on boat with JT BU Burton, neighbor of SP CG Colonel Saul Grangerford, quality gentleman CS Colonel Sherburn, store owner who kills BO DH Deacon Lot Hovey, friend of PW DR Doctor Robinson, skeptical doctor DU The Duke, thespian swindler HF Huckleberry Finn, free spirit and narrator HI Hines, husky debunker HS Harney Shepherdson, young member of clan feuding with CG HT Brer Hightower, friend of AS and SP HW Harvey Wilks, English `dissentering minister' JG Joe Grangerford, cousin of BK JH Joe Harper, member of TS's gang JI Jimmy, young member of SP household JK Jack, slave of CG assigned to HF JL Judith Loftus, woman not fooled by HF JM Jim, runaway slave of MW JN John, on skiff with MP JO Joanna (the harelip), youngest niece of PW JP Jake Packard, thief wants to drown TU JT Judge Thatcher, prominent man in HF's village JY Johnny, JM's son KI The King, distinguished rapscallion LB Levi Bell, lawyer and friend of PW LI Lize, slave to AS LZ 'Lizabeth, JM's daughter MA Mathilda Angelina Araminta Phelps, daughter of AS and SP MC Miss Charlotte Grangerford, eldest daughter of CG and RG MH Mrs. Hotchkiss, friend of AS and SP MJ Mary Jane Wilks, redheaded and `full of sand' MP Mr. Parker, vigilante MR Mary, cousin of TS MS Miss Sophia Grangerford, second daughter of CG and RG MW Miss Watson, unmarried sister of WD NT Nat, slave of SP who feeds JM OD Old Doctor, doctor who treats TS PA Pap, ne'er-do-well father of HF PW Peter Wilks, recently dead man RG Rachel Grangerford, wife of CG RH Reverend Hobson, Baptist preacher SD Sister Damrell, friend of AS and SP SI Sister Dunlap, friend of AS and SP SP Silas Phelps, cotton farmer married to AS SR Sister Ridgeway, friend of AS and SP SS Sid Sawyer, quiet half-brother of TS SU Sister Utterback, friend of AS and SP SW Susan Wilks, sister of MJ and JO TB Tommy Barnes, little member of TS's gang TC Tim Collins, `young jake' en route to Ryo Janeero TF Townfolk, crowd of people TG Tom Grangerford, second son of CG and RG TP Thomas Franklin Benjamin Jefferson Elexander Phelps, son of AS and SP TS Tom Sawyer, adventurous friend of HF TU Jim Turner, tied-up thief WB Widow Bartley, friend of PW WD Widow Douglas, `allowed she would sivilize HF' WW William Wilks, deaf and dumb brother of HW and PW 1:TS,HF;JT;WD,HF,MW 2:JM,TS,HF;TS,HF,JH,BR,TB 3:WD,MW,HF;TS,HF,JH,BR;PA 4:WD,HF,MW;HF,JT;HF,JM;HF,PA 5:PA,HF;PA,JT;JT,WD;JT,HF 6:JT,PA;PA,HF;JT,HF;PA,WD 7:PA,HF 8:PA,JT,BT,JH,TS,AP,SS,MR;JM,HF;JM,MW;MW,WD 9:JM,HF 10:JM,HF 11:HF,JL;PA,JT;JM,HF 12:JM,HF;TU,JP,BI 13:BI,JP;JM,HF 14:JM,HF 15:JM,HF 16:JM,HF;HF,MP,JN 17:HF,CG,BG,TG,RG,BK,MC,MS,BE 18:HF,CG,BG,TG,RG,BK,MC,MS;BK,HF,HS;BS,BD;HF,JM,JK;MS,HS;BK,JG 19:HF,JM,KI,DU 20:HF,JM,KI,DU;KI,TF 21:HF,JM,KI,DU;BO,CS,TF 22:CS,BH,TF;HF,KI,DU 23:KI,DU,TF;HF,KI,DU,JM;JM,JY;JM,LZ 24:HF,KI,DU,JM;KI,DU,TF;KI,DU,HF,TC;PW,HW 25:KI,DU,MJ,SW,JO,TF,PW;KI,DU,BN,AB,DH,WB;KI,DU,HF;TF,AB,DR,MJ,SW,JO 26:KI,DU,MJ,HF,SW;HF,JO,MJ,SW 27:HF,PW;PW,MJ;WB,KI,DU,TF,PW,HF,RH 28:HF,MJ;HF,SW,JO;HW,WW,TF 29:KI,DU,HW,WW,TF,DR,LB,HI,HF,AT;HI,TC;HF,JM 30:HF,JM,KI,DU 31:HF,JM;HF,DU,KI 32:AS,LI,HF,SP 33:HF,TS;HF,SP;AS,JI;JI,LI;AS,SP,TS;JM,SP,BU;BU,TF;TF,KI,DU 34:TS,HF,JM,NT 35:TS,HF 36:TS,HF,JM,NT 37:TS,HF,AS,SP,MA,LI;TS,HF,NT,JM 38:TS,HF,JM 39:TS,HF,AS;AS,TP;AS,SP;TS,HF,JM 40:TS,HF;HF,AS,TF;TF,HF,JM,TS 41:HF,OD;HF,SP,AS,MH,SD,SU,BP,SI,HT,BM,SR 42:HF,AS,SP,TS,OD,JM,TF;AS,HF,TS,AP,SP;MW 43:HF,TS,AP,AS,SP,JM;PA,JM * End of file "huck.dat" jean.dat0000444000175000017500000002042510601476216010613 0ustar dondon* File "jean.dat" from the Stanford GraphBase (C) 1993 Stanford University * Les Mis\'erables, by Victor Hugo * This file may be freely copied but please do not change it in any way! * (Checksum parameters 437,862053837) AZ Anzelma, daughter of TH and TM BA Bahorel, `Friends of the ABC' cutup BB Babet, tooth-pulling bandit of Paris BJ Brujon, notorious criminal BL Blacheville, Parisian student from Montauban BM Monsieur Bamatabois, idler of M-- sur M-- BO Bossuet (Lesgle), `Friends of the ABC' klutz BR Brevet, convict in the galleys with JV BS Bruneseau, explorer and mapper of the sewers of Paris BT Baroness of T--, friend of GI BU Madame Burgon, new landlady at Gorbeau House BZ Boulatruelle, former convict and road mender in Montfermeil CC Cochepaille, convict in the galleys with JV CH Champmathieu, accused thief mistaken for JV CL Countess de L\^o, distant relative of MY CM Combeferre, `Friends of the ABC' guide CN Chenildieu, convict in the galleys with JV CO Cosette, daughter of FN and FT CR Courfeyrac, `Friends of the ABC' center CV Cravatte, mountain bandit DA Dahlia, lover of LI EN Enjolras, `Friends of the ABC' chief EP Eponine, daughter of TH and TM FA Fameuil, Parisian student from Limoges FE Feuilly, `Friends of the ABC' political idealist FF Fauchelevent, aged notary of M-- sur M-- FN Fantine, lover of FT FT F\'elix Tholomy\`es, Parisian student from Toulouse FV Favourite, lover of BL GA Gavroche, young urchin living at Gorbeau House GE G\'eborand, retired merchant of D-- GG G--, former member of National Convention GI Monsieur Luke Esprit Gillenormand, grand bourgeois GP George Pontmercy, father of MA and son-in-law of GI GR Gribier, new gravedigger at cemetery GT Grantaire, `Friends of the ABC' skeptic GU Gueulemer, Herculean bandit of Paris HL Madame Hucheloup, keeper of Corinth Inn IS Isabeau, baker JA Javert, police officer of M-- sur M-- JD Jondrette, father of GA JL Jacquin Labarre, innkeeper of La Croix de Calbas JO Joly, `Friends of the ABC' medic JP Jean Prouvaire, `Friends of the ABC' poet JU Judge of Douai, judge at the court trying CH JV Jean Valjean, thief of bread LI Listolier, Parisian student from Cahors LL Old woman 2, landlady of JV in Paris at Gorbeau House LP Louis Philippe, Orleans King of France MA Marius, grandson of GI MB Mademoiselle Baptistine, sister of MY MC Marquis de Champtercier, ultra-royalist miser ME Madame Magloire, housekeeper to MY MG Madamoiselle Gillenormand, unmarried daughter of GI MI Mother Innocent, prioress of Convent of Petite Rue Picpus MM Monsieur Mabeuf, prefect of church MN Magnon, servant of GI MO Montparnasse, genteel bandit of Paris MP Madame Pontmercy, younger daughter of GI MR Madame de R--, Marquise de R-- MT Marguerite, old lady who teaches FN to live poor MV Madamoiselle Vaubois, friend of MG MY Monsieur Charles Fran\c{c}ois Bienvenu Myriel, Bishop of D-- NP Napoleon, Emperor of France PG Petit Gervais, a small boy in D-- PL Mother Plutarch, maid of MM PO Old woman 1, portress of JV in M-- sur M-- QU Claquesous, night-like bandit of Paris SC Monsieur Scaufflaire, keeper of horses and chaises in M-- sur M-- SN Count ***, `philosophic' senator SP Sister Perp\'etue, stout nun at infirmary in M-- sur M-- SS Sister Simplice, saintly nun at infirmary in M-- sur M-- TG Lieutenant Theodule Gillenormand, soldier and grandnephew of GI TH Th\'enardier, sergeant of Waterloo and keeper of a chophouse TM Madame Th\'enardier, wife of TH TS Toussaint, servant of JV at Rue Plumet VI Madame Victurnien, snoop in M-- sur M-- XA Child 1, son of TH sold to MN XB Child 2, son of TH sold to MN ZE Zephine, lover of FA 1.1.1:MY,NP;MY,MB 1.1.2:MY,ME;ME,MB 1.1.3:MY 1.1.4:MY,ME;MY,CL;MY,GE;MY,MC;MY,MB 1.1.5:MY,MB,ME 1.1.6:ME,MY 1.1.7:MY,CV;MY,MB,ME 1.1.8:SN,MY 1.1.9:MB 1.1.10:MY,GG 1.1.11:MY 1.1.12:MY 1.1.13:MY 1.1.14:MY,SN 1.2.1:JL,JV;JV,MT;MR,JV 1.2.2:ME,MB,MY 1.2.3:ME,MB,MY,JV 1.2.4:MY,JV,MB;MY,JV,MB,ME 1.2.5:MY,ME,JV 1.2.6:JV,IS 1.2.7:JV 1.2.8 1.2.9:JV 1.2.10:JV 1.2.11:JV 1.2.12:MY,ME;MY,JV 1.2.13:PG,JV 1.3.1 1.3.2:FT,LI,FA,BL 1.3.3:FT,LI,FA,BL,FV,DA,ZE,FN 1.3.4:FT,LI,FA,BL,FV,DA,ZE,FN 1.3.5 1.3.6:BL,FV;FV,DA 1.3.7:FT 1.3.8:FT,LI,FA,BL,FV,DA,ZE,FN 1.3.9:FV,DA,ZE,FN 1.4.1:TM,FN;TH,TM,FN 1.4.2 1.4.3:CO;TH;TM 1.5.1:JV 1.5.2:JV 1.5.3:JV 1.5.4:MY 1.5.5:JA 1.5.6:FF,JV,JA 1.5.7:FF 1.5.8:VI;FN 1.5.9:VI;MT,FN 1.5.10:MT,FN 1.5.11 1.5.12:BM,FN,JA 1.5.13:FN,JA;FN,JA,JV;JA,JV;JV,FN 1.6.1:JV,FN 1.6.2:JV,JA 1.7.1:SP,SS;JV,SS;JV,FN 1.7.2:JV,SC 1.7.3:JV 1.7.4:JV,PO 1.7.5:JV 1.7.6:SS,FN 1.7.7:JV 1.7.8:JV 1.7.9:JV,JU,CH,BM 1.7.10:JU,CH,BR,CN,CC,JV,BM 1.7.11:JV,BR,CN,CC,JU,CH 1.8.1:SS,JV;JV,FN 1.8.2:JV,FN 1.8.3:JV,FN,JA 1.8.4:JV,FN;JV,JA;JA,FN,JV;JA,JV 1.8.5:JV,PO;FN,SP,SS;JV,SS;PO,JA;JA,SS 2.1.1 2.1.2 2.1.3 2.1.4:NP 2.1.5 2.1.6 2.1.7:NP 2.1.8:NP 2.1.9:NP 2.1.10:NP 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.17 2.1.18 2.1.19:TH,GP 2.2.1:JV 2.2.2:TH,BZ 2.2.3 2.3.1:CO 2.3.2:TH,TM 2.3.3:TM,CO 2.3.4:TM,CO 2.3.5:CO 2.3.6:JV,CO 2.3.7:CO,JV 2.3.8:TM,JV;CO,TM;JV,TM;JV,TH,TM;TH,TM;EP,AZ;EP,TM;TM,CO;TH,JV 2.3.9:TM,JV;TH,JV 2.3.10:TH,JV 2.3.11 2.4.1:CO,JV 2.4.2:CO,JV 2.4.3:CO,JV;JV,LL 2.4.4:LL,JV,CO 2.4.5:JA,JV;JV,CO;JV,LL 2.5.1:CO,JV 2.5.2:CO,JV 2.5.3:CO,JV 2.5.4:CO,JV 2.5.5:CO,JV 2.5.6:CO,JV 2.5.7:CO,JV 2.5.8:JV,FF 2.5.9:JV,FF 2.5.10:JA,TH;JA,LL;JA,JV 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.6.6 2.6.7:MI 2.6.8 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.8.1:JV,FF 2.8.2:FF,MI 2.8.3:FF,MI 2.8.4:FF,JV;JV,CO 2.8.5:GR,FF 2.8.6:JV 2.8.7:FF,GR;FF,JV 2.8.8:FF,MI,JV 2.8.9:FF,JV 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8:JD,BU;GA,BU 3.2.1:GI 3.2.2:GI 3.2.3:GI 3.2.4:GI 3.2.5:GI 3.2.6:GI,MN 3.2.7:GI 3.2.8:MG,MP;MG,MV;MG,TG;MG,GI,MA 3.3.1:BT,GI;MG,GI,MA 3.3.2:GP,MP;MG,MA 3.3.3:BT,MA 3.3.4:GI,MA;MA,GP 3.3.5:MA,MM;GI,MA,MG 3.3.6:MA 3.3.7:TG,MG;TG,MA 3.3.8:GI,MG;GI,MA;GI,MG 3.4.1:EN;CM;JP;FE;CR;BA;BO;JO;GT 3.4.2:MA,BO;MA,BO,CR 3.4.3:CR,MA;EN,CR,MA 3.4.4:GT,BO;JO,BA 3.4.5:CR,EN,MA,CM 3.4.6:CR,MA 3.5.1:MA 3.5.2:MA 3.5.3:MA 3.5.4:MM,PL 3.5.5:MM 3.5.6:GI,MG;GI,TG 3.6.1:MA,CO;MA,JV,CO 3.6.2:MA,JV,CO 3.6.3:MA,CO 3.6.4:MA 3.6.5:MA 3.6.6:MA,CO;MA,CO,FT 3.6.7:MA 3.6.8:MA,CO 3.6.9:MA 3.7.1 3.7.2 3.7.3:GU;BB;QU;MO 3.7.4 3.8.1:MA 3.8.2:MA 3.8.3:MA 3.8.4:MA,TH 3.8.5:MA 3.8.6:TH 3.8.7:TH,EP;TH,TM 3.8.8:TH,JV,CO 3.8.9:TH,JV 3.8.10:MA 3.8.11:MA,EP 3.8.12:TH,TM 3.8.13:MA 3.8.14:MA,JV 3.8.15:CR,BO 3.8.16:TH,TM,EP,AZ 3.8.17:TH,TM 3.8.18:TH,TM,JV 3.8.19:TH,TM,JV 3.8.20:TH,BB,GU,QU;TH,JV;TH,QU;TH,BB,GU,QU,JV,TM;TH,JV;TH,TM;TH,JA 3.8.21:TH,JA,BB;TM,TH,JA;JA,BB,GU,QU,MO;JA,JV 3.8.22:BU,GA 4.1.1 4.1.2 4.1.3:LP 4.1.4:LP 4.1.5 4.1.6:EN,CM,CR,GT 4.2.1:MA 4.2.2 4.2.3:MM,EP 4.2.4:MA,EP 4.3.1:JV 4.3.2:JV 4.3.3 4.3.4:JV,CO 4.3.5:JV,CO 4.3.6:CO,MA 4.3.7:JV,CO 4.3.8:JV,CO 4.4.1:JV,CO 4.4.2:MM,PL;MO,JV 4.5.1:CO,TG 4.5.2:JV,CO 4.5.3:CO,TS 4.5.4:MA 4.5.5:CO 4.5.6:CO,MA 4.6.1:TM,MN 4.6.2:GA,XA,XB;GA,MO;GA,XA,XB 4.6.3:BB,BJ,GU,TH;BB,BJ,GU,TH,GA 4.7.1 4.7.2 4.7.3 4.7.4 4.8.1:CO,MA 4.8.2:CO,MA 4.8.3:MA,CR;MA,CO;MA,EP 4.8.4:EP,BB,BJ,GU,TH,QU,MO 4.8.5 4.8.6:MA,CO 4.8.7:GI,MG;GI,MA 4.9.1:JV 4.9.2:MA,CR;MA,EP 4.9.3:MM,PL 4.10.1 4.10.2 4.10.3 4.10.4 4.10.5 4.11.1:GA 4.11.2:GA 4.11.3:GA 4.11.4:CM,GA,CR,BA 4.11.5:CM,CR,BA,MM 4.11.6:CR,EP 4.12.1:HL 4.12.2:BO,JO,GT,HL 4.12.3:GA,BA,CR,BO,EN,FE,JO,GT,JP 4.12.4:BA,CR,HL,GA,EN 4.12.5:CR,EN 4.12.6:EN,CM,CR,JP,FE,BO,JO,BA 4.12.7:EN,JA;GA,JA;EN,JA 4.12.8:QU,EN;EN,CM,JP 4.13.1:MA 4.13.2 4.13.3:MA 4.14.1:EN,CM,GA;EN,CM,CR,BO,JO,BA,GA,FE,MM 4.14.2:MM 4.14.3:EN,JA;CR,EN,JP;CM,JO,BA,BO,GA,MA 4.14.4:MA 4.14.5:MA,CR,CM,BO,GA,EN;EN,JA 4.14.6:MA,EP 4.14.7:MA,GA 4.15.1:JA,CO,TS;JV,TS 4.15.2:JV,GA 4.15.3:JV 4.15.4:GA 5.1.1 5.1.2:EN,CM,CR,FE,JO,BO 5.1.3:EN 5.1.4:EN,CM,MA;MA,JV,EN 5.1.5:EN 5.1.6:EN,JA;EN,JA,JV 5.1.7:EN,BO,CR,CM;EN,BO,CR,CM,GA 5.1.8:MA,GA;EN,CM 5.1.9:EN,JV 5.1.10:CO 5.1.11:EN,GA;BO,JV 5.1.12:EN 5.1.13:EN,CR 5.1.14:CR,BO;BO,EN 5.1.15:GA,CR 5.1.16:XA,XB 5.1.17:EN,CM,CR 5.1.18:EN,CM,BO,CR,FE;EN,JV;JV,JA 5.1.19:JV,JA;MA,EN 5.1.20 5.1.21:EN,MA,CR,BO,FE,CM,JO 5.1.22:EN 5.1.23:EN,GT 5.1.24:JV,MA 5.2.1 5.2.2 5.2.3:BS 5.2.4:BS 5.2.5 5.2.6 5.3.1:JV,MA 5.3.2:JV,MA 5.3.3:JA,TH 5.3.4:JV,MA 5.3.5:JV,MA 5.3.6:JV,MA 5.3.7:JV,MA 5.3.8:JV,TH 5.3.9:JV,JA 5.3.10:JV,JA 5.3.11:JV,JA 5.3.12:GI,MA 5.4.1:JA 5.5.1:BZ 5.5.2:MA 5.5.3:MA,GI 5.5.4:CO,MA,GI,JV,MG 5.5.5:JV 5.5.6:GI,MA,CO 5.5.7:MA,JV 5.5.8:MA,JV 5.6.1:MA,GI;MA,CO;TH,AZ 5.6.2:MA,CO,JV,GI,MG 5.6.3:JV 5.7.1:MA,JV;MA,JV,CO;MA,JV 5.7.2:MA 5.8.1:JV,CO 5.8.2:JV,CO 5.8.3:JV,CO 5.8.4:JV 5.9.1:MA,CO 5.9.2:JV 5.9.3:JV 5.9.4:MA,TH;MA,CO 5.9.5:JV,CO,MA 5.9.6 * End of file "jean.dat" lisa.dat0000444000175000017500000034011610601664607010633 0ustar dondon* File "lisa.dat" from the Stanford GraphBase (C) 1993 Stanford University * La Gioconda by Leonardo da Vinci, digitized in 360 rows and 250 columns * This file may be freely copied but please do not change it in any way! * (Checksum parameters 1800,290679695) XlH3?TwGpcWL\NNS_ZWoWMo|gTVz*VSyWkBU0qmQTvJj=P.6_pOifKqQd41JTvJn4VPRiWQBhA, Ra915Us:XCQc/o2U0737R~66UTU&UUYHDU:UtCjDQ@v-1SXWtER^sVATx4HdTwG@iW-l~?R^aD7 T3J,5SX*9OU0PaHRbFIXTUAt3Q@@+\Q,RnERaseAR8@|2QBY7,QBF#zP.GP\SXNeAR^sVAPf1Bx Sx?nvN#_\kNl8jgO?;TtP.71@O?xKpP.6=@PEJZnN#i@pQczG.SXft9Pft&wNk>aYM-C0TNl8aY Oi-uwOGrjX2iw ZD,m-UZSXNk6RbXRZU=j?lU=RcYR^R76URzaDRaID3Q@vz%PgPY+RZ>-3S5s73U0GF9QBY4. R90JEVOv!ORajV9U0POHS6O9UTwPvcW-2DqUszUNXkk|VS6E~PR^\~OVrpeyUteZtWo2e,RaI76 SXftDT3S/AT3JUT3cRPSYBOQTwG$lV|N#jVP9WgWMN?pZE0CrS6N%RUS4HpYHDCyW-$3-XJ#%;Sy;~E T3cOOSy-,DTwGaTU14QpTv=RNUS3\TQ@Ut|R8@<TU#UUVQFb;WM.bvU=RidU07LKSXft3P.h_8R8+GGTUlOSWLs8ORaaD1S6Ee4TVYvO R^!zQURqvUT3#^eTw?8gYG_9-YH46yU=R;mV\K:;ZD,9wSy*6TU0q\iX%#JkX$oCAYin^>UtL>V SzKgUS_8ExX#rG0aA^s.VPRZfU<,KXRaRD4S65h4PfkuwPEJrzQdCu2T3T6GS65e8R^jCR9II>O?-r, QB:V:PE1WrO!AuzQCVABU0q@RQ@Ut=USU$LR8@t%SW!0|Q@Le?PfkoqPEAWoMnpzNNJmCTM-BJDcx|NDV\K<. Vs3SCb~fw\Twr8aQ@d:>R8@,/R^Q+ad$_mb^||jab^@>XJ>bK dw:j*dW4FNhlYfHetms*fq\F3i?CP>Z.FR0U=9leQ@d?1Ra8=1R8mu0T3lUIQBzk=TVY!SR8@:\ T4_fjR^!tJRZ>|4T31+JWMy7hRa^/UTw7RIRZ>+|P;<|,Pf&1.P;%_vO?-QjMn.X#iM3Z.p$MXk=nvV|.~-0T3B0JR^R4CVPIcWQdCn-T3#yWQ@Lh# RbFCSR^^nHSXWnCT3A%BTVGmMRa<9VTwPmMRaR9\QBhJ?O?xc_QBFxpOiV>eM-ULZN#i^hN%$6N NlQsbN#!5p2oy UT=MAV|5;mU=a;fR990=T4Z2ZS_7*bT3&pTSXf,7OifT~R8mtPZ.p.JWoBn;W-vA5a@>rbbAo+?eSUm.bZcd3ZEkIF b98WSZh,b#c5rZUad@&mba^DzdVPR:a@K$RZfj>>URqgLTU#scU0YjeX#PhbP.hP:Q,9eCP.GA! S6EnDS5\hBSX*3GRajeARaRJ4T4_lxX#q,rVO$$HP.7D$O?W8xR_@JtPESipOG_yeOG_yfN#8OS Mo3CZO?-ct2ov WORFLV|N|tU<+KWR8@,\S_85cUuR&mT3lgaRZvw$O!J|$P.Pf9US3.VTwGyeV|FG$ac*vEY?,Uw S6EnITv=aWWng1xX#_Y3YiMh6cy;Ape06nnZEJ04Woc<D1Z.OpNcyx1reeRpqbae0A: g-&fRgqW0~i?LfFfPZ@,c6NGudwqR@b9sApa@m5BWo>e2UubY?VPs#wYhhYbQBqS$Q@m|0QBYH4 Voy@>Rase8Q@U&1Q@dz/QBzb$Qe9%aV/\_lVOUpLQcX|&PfSiwPft_vO?-fyPEAWpPEAQjNlHpc NlQ@nP.P4y2oz XKQS0XJuVnUbar&dbZ=\6 SzBFMURqgQSzBFTXJ#+$X#rF=Z.OpTe0g/uc5_8BZES07ZghTvfOm6uet@+4hlPZLg+-H@b~x=0 hK7oXl&A4_GeZgO@RadLHedw6Yeb8l\JY!bvlgME5$e=jgKi!0bih#X=*c7BG9 jf^qkn4XW6l^Y._gMo^Lih:E4etLIzcY1ezgnD@~admidZ.:ThY:/~uSy;:BR8,1HUsp\/PESrv PEJr^P.hk\QBhA&QBP1^Pf~|;Pf#4?Q@,PLVOLgJQ@Cu0P.71&O?-csPEJitO!J#,Qcz7yN#QaV Nli*mOioWv2x~ ZEI:0Y?njBGZg/cgaBy!acW|ujcye?YZE%^fgMfd8ii,4UiI0+vf|WQ$frEmF jfaJTf|WdCl97.&eu9OCiim-Le1C:4eW-Bq$XJ&qw U0?HuY?eI:W+yM;XJuz$b8>EMet>*/c5_ukcXWG^eR:9xZ.g*ggnxgFkd12:iiCiIh#/lD c5ZNMU0GdZZEkC6ZFF$Qb~fn~e>6Kik9$E4fqQd^d2wW7Wo@bAVOCaISyfe9T3uXWUPfblvO!c4$RasY9URzjIR8de#PfSr_O?-QoQdLn#Q@m~|QB6_sPfkfl NJ>sjPftuq2lu YIuKpb_#!IZ.FR2Tw7yiW-&/^TU=jVURqgOT>%<9R^\e9S5sG8Us/mQWnf&nX$4j+U0|NrU=9&y WMpS&XKZR|WL!ffV|ptegnxjHl^:\?l^?H*kB5GCc5Ylx M+ynDIW!ynI5!\_L|X+VQCwtbaDIxCihg>7e0:U:d2+i9Z.p@GVP9WhVq^lbT3#jUU<,5HPfkux O?xHsR8$/T31%FR834xQc:Aw N#\KuPft_x2ry dwhR!acpjEY?+%0UtM5hVrFAmR~EzESXo/RURSzcXQQ@Ut/S5^A0RaRSFV|XApU=s?kTw?No Vr64vV\L6=W+x#gTW4x,Y?nX\b^_uYZ.p$Sa@B>VaBX^VaBXpUeRqYBmX,o\lZ#Xreri;gIWQ~3 7Z,\D78Vv46&|c>6&|gACo,udN$pEIZ?0P.e1nI7dUb7cd2eN9ZE9:1W-cesU=LQA 7Z,*B8W~HA7Z,!45;u.!6&rH\AuKvWP!h@GfsT&XfPHj;d>~4VaByyHWo2ezWol<\Yg0$2QBX=^ PE1QsP.77-TUk+4QdCh?Pg7i4Q,9hHP.z-BQcqG$RZ$h~QBqY*QBF#_Q@,47Ut,5OS65U>R8dPx PEk;xN#_>q2_; ba8BJXlZY6Z;%I6XKQb9Yh|C&R~5wKVP9fxZDL~qU0/\USXWe6RZ>=0RaaYGWMyq/XJc,|YHDX# Woc/\Wny1yYGY,~Vryk&ZES34Z.:Bab^_&db^rcYa@mZZadB*SeucX-JeZ.OpJXlZC:VP,+Pf#7?O?;c^SzTLMUSeHXS5!V3R8dS_ PEJitN#s2x2?# aA\$$YHn%7ZfkC8XmNIUa&g5+SzBLRVPs?yXk&btUtnHXR^jA2RaaP5T3cIOW-Bt*WMF1~X#7t~ V|XP?Yhzt&YGY+&VQXe@aBpyNbarxdb~EDnb^ZQVb^rlVZ.OmRg*P|kgmhX,Egcyb6&?W=6cra3 78Vv36&?T<6crT=5fKv%6cP\.4?XI~6cQUdQgeHGfP!X3e=H^xXlQF\W+x;iUte8aWLj5JQB:h+ Qd3o4URqjSU=0NZSW?:,Qd3e%P:VG9USMQoUS3!UUS3!RP.71;QBYA:T3updTwh>iR^RA5RZ>w; PEJitOHEW;2-- YGPepYHx09X#|,FZFPHjb_&p1U=9oeTxMu,XJ21tUtnEYS65V5R~EwATU=mVYHDF&TwGpbYiVU$ VqjuqWMExmX#7:!X#?9+bZ&>Oa,i=aZE<^Tb8$KWZ;j/Aad2\Ug-u81Sw#7_6cra27Zny26crX0 6&|c>6&?Q/5fBj,5fT@+6&?T|5DyLy5D.d?A3IWLbbo+$h$nVRYh|0&VOm$aXJ&bpSz1RZ$w$S6g0PT3>5sSyNY5S6ft3 QBF?uO!lT331* Xl7w_ZEtLEWo37Ga,j4fZfIs*U=IibUuSA%ZC/,!X#YtnR^sM9TUb+FVPIigU=s-qTwG!qac.U1 VPauhTU&geX$4q8acE.>bZ$BUbbfhiYI1jcdv<,_aAah0c64=re8TwP$oaBgQ%+6R98<\T3&dTTWf1&URYOGU0:yJ R8dV^QB/?72|+ Yi+/6Zf~CFY?o6La,H_dY?eI.U=0NbVPR;_ZDnU~GjeSm$?ZDmY09TgQ76cra385%rN6&|c> 6cQ2+6cQ5+5D.j?5D*p!5;$2*6BHB+5DpFx6cQ8*DM-O6h$M1Re<2nQWo2k?Zg6gBV/sNSTw7LK URqUKT3TLPSz&\lTwGjSUtU*ST4QK;Z.O.RY?3hhU14WuURYULSz1,6WMyAqVrgk~Sy;zBUtd$L SyfR|QBzk=2-$ aCmWQYiMM1a@K>Sa@>ofY?M0zU0:^QTU#vfV|gk/YiVFzUs:jOTUkX#Z6*W+yM-ZDnF.Y!t.hdU<:xb~fk&fPaL9gMfg6eS@*tQAH\l78Mp26&|g285 6&?N-6ciE#5fKy:4?ga?5;u.%5;$2+5DyLy5D*g.7a$Dvh$eJagLZ3bZ.y.Jac.^MX%#GdT3#vb UF|CadvfbacNdoSzl@mU0P@oWMNxbVrFJ&XJ&wyRZ>-1UtLmA Sz1:8Q@Ln/2#$ cy-7ZXk&k.a@vTaa@>obYi43yUtd*OU0q.dVrOq=Yi46~VO+KgVqRQjb8lr=U 6&?K#5EBp,6B7*.4i6F,5fKp;5fKp;5DyOz5D.g:6BHFcYKM?FhJJT:dx3Rub^ZTOXK7:sT48Eu U<,EkW-c~:Woc/\Y?+,0Y?,X%W-$eHcY1VhY?U/pR^<3gUVPk4~ Wnx;mXJu,?YH;6Db8>NMaA!y$Yi-LRfrNZ!Yins+R^ZWZD+a-adduwgM-@1a&*d1SX*msXI*J:WnW&gU=RoaR~X0SWLs5RU0/\b YGGYqWL\fW2?? cX-JbY!0/0VrFM?aBFL9aB.$PY!0^*Ute#=b8c*Rb8u>EXJcPxWod17e0X~na@c*IZftCIdw6MK Vq^;uW-BAxX%uMsXJu&0dx3@%cz~:veSU@/cY#:Mi?Uo1N?Z5785S5B78Mm06ciR8Eg$5h7Znr= 6BHE\6A&^!6B7>!5fl*+6B85-6A#@;5D.X&6A&y,5fKp,5fvj.jFOO_jgOFsgnoB!aBXjBYiwm* X#_F%V|.bzWoT|1YiMd\WNBq!b~y3=e1m>>acgO0SX*jhV|XkTSYKLMWno#mVPRlm Yi40xWME?o2|# d2VfOa@m8HVq~4!a&.d7Zf%dLY!0+0VqtN1a@d8Tb8cc#Tw_BuY!bXQe0:FtZ.p>Ua@$Zadvau9 WoKw#XJTJwX%uPxYHMwHd3Stzd3knreS@**e29@XfqZmLFBKIX92XWB7^9$26&?aAEgu.b6&?T\ 6ciK|5fBj:5;&*%5fl$#6A=*#5fBd~5D*g,6A#@;5fKp;5fTzDXp;kUjgF9xjf\VQfp>6ca@l!A XlQI>V|gJwWnyArVP9WhXKQS5cY&j>cYc6$a&.N=Sy*LaX$D%FX%c1nV/\rZR~pCNVP%A_W-leu YiVX%Wnf;g2-$ cy4rddx3RpV|.~3mb^iQQb98lcc6fGgZDm/tUSDW@baQxkb8>fab~.9>dUtktdwXqY Yin%BYhhqxW+;-qba\1nb8>cYaddZne1Lz7jf!qhh!BR%6crX06&|c=6&|Z=6crvNBo?AF6cQ2# 5fKp;5fKp;5fTv;4?XFz5D.a&5D.a&5fKp;5D.X&6B7\!5fT^?7@vidUtkufO$Or ZE%vQY?ML:W-27_etdLycY1z~cY1w#gMfmKjEISgd|cq>6ciK|6&|Z<6ciK|6@;;zFCiBg6&|Z< 5;#\$5;$2-5;u!!4?O6w4?gO_4?gR,6A#.!5fBg&5D.X&5fKm@5;+juj.*<7oRN;%jC.8aZ.*>L XJ>9/a@m2MYHMj+U<,KYXmWCMcYk/wb~;nfUtnQmUSV*4du,ZDXKih0XJ&,yRat6jVPIlrV|5=p Yj137V|.Vn2|- d47e4iHn7UhJuHNgoKEOgMD*,dVhCraC$=sb^G\8T>#0ETw_Q,czJnxdwYCzb8>ijdU=0.fP\W< czbqsacpXDZDnP4etL9zfrEj7frNvLjfjbnkBN#5fBd^4?XI^6A&y,6BHFkg/4<~7adVY3tbAEehVq<7@XlrhBZ;%5>XmD%5XJ&,&Raj+aV/\;wW-2Gz aBO90YiMR,386 et,9JjD+Agk&onbi!IeXgo1Z-e1U$+bbNbpaC2!DU<,TjXKZY8b~We^cX-,;d3Sk^e<@^|f\28A dVP9;cye_baA%9Nes:0@hlhfLhK?DpmXed0k@lsiKp7Mi6cZB*6crmJDm6bvOHfl!W-#,wP;S8V Kl7;VAcYA%*h$3|UgnxQ*dU%qjXKQY1Y!A9KdVP0yc6WPzfP^mOjg5/mhlPK8 dU<~udv6,cY|MNM6Be20^enw/1Mmz%S+d3k4C SW_/#OGiXQIxH~CARQbV7Znr<5;u$$6B85*5;u!?4?XF~5fKs?6B82-AxD^&k@TIkg-&23fq\U0 c5iTcfP!gBg-bo|g*h;IeRq9,euRN=bbNbpaA!<4b9HcbZ.6E%Szc>:cyniCX%=n,W-vMBa@u*H golB5dVY0j3cM e0qR\hLf6%m6w.3je>&9cYJ:.gNK5GhKh&Ld3SYjZ.y>RZ./QgcYSwxdUtw@etd:9hKY&Tf|5E< dU%tqd3=6&aBgySdWVjVjEjwpk,ZT*kCKs!jdVsL7Zns6DmX,%VsL/,n3~KBoS@/nj.gy*fqZUg SyEC*Oi4@bJufaQBOo6i92giC6ciE-6B85*6A=\#5fBg;5fKp:5;u!#5.#_knUal~ii$#7e<>@+ b^HKjg-S!ChlhZFij9hWh#7BCe0:X?bbNbmaA^.>ba8TdZf9a^Uu9=!c5|fQWo2w-W-v75ZEIy* gol86fPQdm3oS bA5n!e2tisl~,>0h#qiDc6D|idw/t3hlq?UbaZucbarxZbAEt@dVqa#g-k\Be<@:0gocEMh#/_Q fO$UzdwqU%bZ>Nae1naSjERYfkC&*^i!~3tcu/4G6&|vTJV8fNTx;xdj..,4qrS,!^#.wIrkj~A duLu@P.O=tK\zulBqJam9T*&M7_+u\6ciK|6ciK/6A&y@5D.a@5fT^%6cRm~mXwf!jE\/Vad$-u aBXsag-Jy6gNKWXiHd#Ri?wAZe13p\d3<,oY?,/DZ;aV|y#1Y?wj- c71wzf|WN@3rQ bAW<6&|Z|5f2Uy4?pX@5fKs?6cia~kCT^?kC2dxadUin a&ysZfq\2?d3=eAhJ=KNiHwPdhJk$7e1U.|Z;~LHZf9~Cb^?uMYHDR~Txn|^aB.j1X$D~4Wnyb# ZFO\cdVP6v3iN e1L,5g-uKQl~42uhKhi8cyeuUdWD9HjfRPjk&-hOfr5jDeSCk0iiUxKh$nqmhKPoSfPa5l^YRJTu,,lIyEjPAs_qY85kB978Mm06&|c\5f2Uy5D*j:6A#.?6ciLXbC8phk@uszcXxYx cxq>Se<$s;c6NM,g-=fRi!RqkiGzQBe1U.=a&yjHY?nw7Zgp>MWn.DnTV7@lZf<8|Wo>F:TwG*t b~oYpcXxMq3?W jEswsjf^eehJS$IiiCP=b^ZcedVzv>e=~5po0E;!jE!+mgM;.Lii,Dah$+:nhK_JqhJt*Be=spD cXxMrdw:%DhKPuUiIJ3@la|T-mzR*1l97sIKpPht93U-3PHT$=&?_PP!NRuv#!|b0-9*!L$Ez:u ,_!u=qn,e1bX?tJKRGomCm%dj9UK|L7Z,.36ciN\6Aum_5EK^%6A#.:6B85=N\Wz*kdBUte0h0z a@d2UcXxk,d3Jhzhl|1YiHeDgjEshXe0/d-b_&.LZgF*WZg/TaX#7z,V|FJ:Y?+j.VQOt\X#7,- ba!GreSv\+3-e jgy%3kBEkef/_<0ii3AeSCm|e1L%3hLW0#m6n_*iHeVoiH,bfg-uljm5zynf\B_ym5?E|l^qph fq\aEhKGxZf|5dEiHwbnkCBs!l~V.6jeA?!9TpWFCNoB1gQCpd%mv3\$:bUS|>3a%\r@p,-~vBL %J\NO&E7jtjeTugNj*3,C+:Bw9UUDT9UK#F6B7>%5DyLy5fc@%6A=$!5;u@?7.2%ek@#*#e\ODa c5iHKaB..LWo>+fhKY;Qd4i3Ni!RVWgM;c+cy-Mjb~f,+d3SbpbZ#>IZEkOFZf%XGXK?+Mdwz6n Zg:fudUS1U3uV keZT,gNT;xkcfzef|fp7hl?1bf|fyDkd$U9nU^?$kCUC5mXnQ&iHwYpmXnNzg*7-xo05\3mz0o? iH3#ejf!~jgMWd9iH3rOjgpg!mXnj0l5j*M6&?X8Ej$/nj%VI*#j_b2+eO^Y|>Ls|*Zfjy%Z:LothlYfFd3#bBi!ReZf|x!5aCCTgcz1e~dVPI!e<@CaXmW3DY!AOSa@>-yeSUa& eT9d9d2VWH3WK ke_u+go&=@n>jl%f|ff36GDTZ+mA&j2%ml+|+,p!X|uzU$\|>s./Vw|X #!Peg&fhytjeToYNkKg=Eh\MXxwl8=psT h#|Duo12-HjEIDPetmg&cZQgjjDLJ>MjsUv5;v6HJxySCkfz1c:vDZu$h1CJ*_4?f/4W1h*6>HL %$1?S^#N1GhInaLM+pL\GA#1ACL%sn92.?P7^9*15DyO^6BH8-6A=\%5fKp@4?:JNl8=@!l6q-j TUJ,BS^y7ae=jsOg*iGqkdcs&jEs%uihp#:b~EDmb9!Vzaej?2dV70~et@/0e=\v8dwGM1h#:H5 hlhH0dwzI~4My ke\ZVn3SKoxSNcHh$n~$p|n:cr-F,xxv|/8pPiVOprogumz0.8n3kBEotl=GiHMAkm6-8En3~H6 kBpC^n3gYLr|LQkE6oP.x#!l%$>D1-9ypH+,gsK+dhlhoRg*7xXe*o=\9no0.&O o0gN4m6el-i!Ihihl?Gpl8JnXjf?bMBpehH6c_$iNKvsiC@70g+;2yetLFua@@rqb98=:gM;^Bg*G?gjD+JggN2QekBo%h go1*IgM4mm3?m k;4!CoSLJ*~gP&&jF:CAoSl|LwZ.|>_oa#0wxy?Ey=rG#tfZo^o=i:mrM9e=mzJELq-%.vot$be o0XH3mX+x+iHV7ci!tF%l_%,Qjf6|nB/9:M6c?$eMp~rxkeBZ46c|>kOJa3>lAX80~H>pk&gM$g&F4\n,9/o0:S#LF &;|5;l_%:EX%u7ZPfAHjMnF9>H_vzHCL~jk92*#I6crQ\5;u!$6cQ2%5DyLy4?gR@L0+QnLO+?H Raje7P:4tcP;~uzTwic+cZ_XHQgo7@k&WbefpBqodU%wwdwzg%b8vcoe>gZejg63xjD+SngnxpK j.N~ejfHb|3-e j:d8=k,HdFqMVwSjFO/FlaiC3y=zcMw_XZ%y-6ciK|5f2Uz4?gO@88%.dK0_iC RZ$q&NJ@joOhiOTNm6HSd4hCLNp6OZdrw~:gn;Wb_M+q=z\k:o:=/$U ;5*iDn>Z.bfOm3kYi3:lQB6ulK_Ld>R?be=8s?nWv:bkDsEZzrDiA&.DdYrHF_jkG\>.,~dV~~jHaVy=YEPr$3>T y<~y7kBg6^mztunxxT;#^J/7eFDDoj6ciE-6ec=NSZsZXm7lA!_N~wZ,9g|r_oa|1zsAzq!Nsxk :tkayqM3/|iG/rKdUkGOT>kj#MMFS3FexG&8yp&J78Vo=5;ly;5fKs:6Aum^4?gO~6e@PIKRh;# PfJ5cNk>RQLPJ79L|//Afo-VqRbz32V-ph7ij9ngh$eeskdTj&jgF6ujgF3vg-&iUhK?Mrl8=^, kBy9mfPr.n3fW b^7$3aDs+\fqZmtZ.XjAb8@5ao3;>skC>@r^K/6Z,72%zmWFot2?y~H@Of~B@4Of|Elq-kn0&.V\k^l*zI@XheXDjgLS6&?Q*6;^0pWO9U,oTa,H^K/9a&.-Kl&g;Nn@C&H0!Ns;p ?|Ua2zPqxcprD+YjfaDNcxzx?PD,$SJT;gD85tZJ6&rH*5;&$#5;ly.6A&v&5D.X^5.^#-IV$~R JV8A-JU2NyJUTl#M-$$wR82#uUR?BwPf1K%j.*azkBp6ul9|y4kdc^:k@&y~h$MAVhK?Ptl8=@; l9G.oc4&.E3WB e\ySJTT-R*P:DnYXl7YiUtC!lgO8;No<2fo~GyPGq%,RIcY1:vbAW|Gz\bPF&:1iy^kr=ayTQM= z|m-vySu&=;Xve:@Ck;tz%oOp42d ?QCm5_-^%yp|V+VjfRAQdv9S\QA%QWIWH:99UL4R6&rE-5;&\+5;u@.4?XCw5D*g@5.INwGBPtP FFrp&J>lQwI5!$&Mo3CaOGZdjRaI,jPDx8:jgyg~kBp6tkC>X1lar2~kC2XyjEjqZe>poul^h.~ kdcgcY?Mw93Q6 Zgy!1S5HnzLr_ROb8&*CU0hjac5\T5mXeaJ^kHuUj@p5wb^?&Xczbq,qQ&X=^KpzHy1KHu_*:Ia ~!|.a^m3Cl;6Ub*?ss?YxMVjhAQco85;u@?6;izbUv|I%p\&>d^m2ji,9zS#;YjJ6:1uXT .2liLy1ufWnU^=\iG:T8c57i.O?VyPI4US7As?tU6&?Q*5;u$#6A=\!4?O6w5EBv!5;>>kD#^oB EIK=_.K0G=/LO+?7KBf75;&!!6;ZnWT5.GrnxwY6y1Ki,_o<~c,~v~/.Vok4.3_nN o\x_~k;4!1jERSZgMfE@ZDn3iNJc/FH_33=BOnDNR@Riel~x5En3%fTwZ6p7;X@e!:St!+vYm_x VOCaOYjSykihz27f/rdlUsO.#LO+t#G&Y40As|%e7Zef|6A=*#5;l@#6A&v@6BHB*5;$@kEh_=$ C-+ZSKtV&7LqbACKs?7.JV8lLM-,TzJwilJUubuXjE!cMMg/fT31zBWoBJWOj25DfrNp6cyxhwgp|DGZf^^vSXWkKfsu=APgr-I@C=1,:Sks\ &gfKw,aFr~?\0He$:9UofjA$1Ar#&A6&|T/6,u-JTycPji!0DSet,CQp8rd KtV#HOHWu=aBgX8ZfIIkP;R>QJUBBmGcGD4A0Hqe7ZMT*5fKp:5fBj;6A=!.5fTy!6BH@iEF>l@ CM:^CJU>Q7M-2zFKt4J;J3@TDMNeW^J3mKGP;SK\jE!/vk@=$;j.X3rjgF6wi!j:rkB*Lke0O:t Z;H%4AP.YxYjiI\2~ilynPDw$YLPAGNO!J#!UtU@IS6fw9R^t6nVqITcSyo,KPftroN#\N,M$\:/K<;1VTxW$A.UiJg _pP0a&Erjg&.M!i@dkJ>PADNB8Wj>46ciK*5:OfOZGL|pU\FXwI6Fj7YmTMA_M+x2u^b08J1iq5 CL<.^EHN-MFg7LsnBmG@-L1N%>LfeUy=wkdKj&i!0Mfkdcs,j.pOzj.g9thjnLd c4t.kP.G4.2xq MM/UfNk$FPMo39WNl\l!UszI7SX;,AT4Zi:TvAd|P.GG*QAkEdN#\W~K\|A!L|OwSOH5gY_Nj=1 y=_c4&F4@Wy1vA|ySbo!L+j*<85b566crQ/5:6HDR990+L*@-kDJn5LNNbX*ysWjnmv$epdQdn15SXoq9RZ$&0VP5D.g.6BHB*6B7>$6B7>%5E2~7CLkXs CMG2@Dk$&+D$gqiG@nUiJwH1;Ku0<+EjB%zN#iscXO57MkCBp:jfjechK_7jjE^%wl8&dnb8m8X XHrsSb6QeQ2Tb MnpwMMMXtKMoLOgUSC$URaR6\QB/_FWnfxPP;~lqN#ZpYKt4P$J3l-:Ha0v_M-U3MLPbYFQ#F|^ rKY33zPGlgpP\@+os*A&M%gE*8W%NA6ciK*6d55UB/t@@J4!hjOhh*.Br^Bto<>PTc>Ctg8yXcB 7~rPQSY&\kPCqaq85R\478Vp07a1NPAR_+jA0Hka8x%535DyR~6B85-6ciK*6A#$!5E2z9Cn6sq BP1jyDkvo!Bq=^KH^jymIz2ZzJTWOLEIC0&N#ZdXXN\1Nkdcv&f|W.Hii+PhiHU|bi?vx6ZEtaN W$QmYb7E3V2NZ L/%YMNk$LSL|O+hYiV6vR_vAqPg_DQUR/sAO?xElNkv3JI5sskGB_9iI668~MMp,KLPkV5Ns*/a qP4?pxu2bylardAmXD2pL+j*/85bBA6ciH-6c_yHARr/;MNdvkKEjK+_Nk$FQUyP5El8=^rae0V*f|WpEi?d_Md311XZf~F4 VpUaPa9K-D2Tj Kto4LOi4vVL|O/jYimR99v@tvC+g.g6ciXGEGaEtkC@H%TsI49v@YcBrPNo4h35f2a,6A&y;6A=\$6B7\!5fKy|CMPB~ AR\6pBqS^;DK0fME?ET3FgZUzI4U3-A p\u*Unu+E%i?M7nih/D|ST?OP6@4m16&|W/5.^csCm~j;MN2ys5;$5|88AIZftQ~vQ_gJK86hk, L0*A%WjPcT6&?X09>>bhF:4p,M-@XNH7zJ5A%5D*j+BP1aq ARr:kCM6$^Bqb$$EGj7*D$6AeJ1_s>AtNa;D$*/yMQW3qlaY$se=^i\aCLWkd314XYG/%xXJlMo T2oeCTu+/v2co LPSPLNli>qNlj8yQcp=^RZ>:=SW_:.NlHscLq9;4MnywFJUBW~HZnChIXN*jH&3oLKR_T2Q#7q= n4F=db5@b#je>rHi:p_#Vm>1k6&|d078Mi\5?CMaK\_B5VQo|AFfT1GH~g>DgqW6uQ_*qS8zwkK T9J\BfnGf*F;&VTHZe3qPFY?CTUSj@Ix\OJARiqX7Znl|6BHB/5D.X&5fT^%5;&$!5D.j/CM6!x BO;UsCM6\zARr%qCM6>~CMhj1GcYA3BqJ!$Dl~JsNnVhfl8#pofPsE*bZ>WdaB*\IXJuJpV/RTo T2fL>RZT-i2Wl MMp%PM-@ykNKRH~Q@LP:Ra015S65F$O?e>cKA@gGBY+hG&/:VJ4j1KK0QQmoWP|_ kC$N&Pd|IYaBysEgoS?=WIr1f6&|j36cQ8+6fH+yPDD@&c8G<3RY*lbM-LFfh+c$Z;8~MJT-!TARr,Z78DW-6B7>$5fKp;6B7>#5;&*%5D.g\DJLT& BPS._Bqb@~As\0rCM72@CMGB!DJ+51C+zg1DJwfiO%9DHj.X0id3SqwbaZ#rZ.p!KW$\EcUbDG@wmoGcz:YJx54FJ2y^*vYwKQ jg5R+L/b16NK^x-bc&y=Tr,LJ78e@46A=$!6g5j:R_mP:U1-7vM$^P&K=1Pbk;M-%P&tGTA=BXT OHW;FfKGQD#IfgOjd&9i?+ShdT+#XaCUfZXmN09WK>sOWMp7q URzsMO?M@c2lq M-L6SMNLynM*9K%SyW9$QBhD|VOLR6PDxHqOi4.eN#r@YFeoK0G@wmlG@MCfJVZu9I5bIwe0/,6 eQ^AsKD#8*ENLE!6m_/6>PB0=YDJwfj Pft_qL/s/N#ivdOG?@eN#\2aD%$@1H9I$tGcz+bIzUB4HZMqIPhy0A Yg~V\KSM?2H~Ff/T_DvQG~I=r78e@46ciH*7DQ;@k@&XKRY*lVI6XyGc7#U:owb$NO.ASkFE$R\ Vr*bdNJm9MKSM|OWP.f:ij9qpfpc0,KQSdA8yFfG78DZ*5;&\-5;lv,5fKp;5;&*$5fBm!As|%f 9UUDYAtELvBqk@&EGQ;%C-d?|DJLT&DJ+HBF.G%tMn|RjhK_DgbZTyHY!b5/W-&%;U1O59U2&F6&|Z|5;&\-5;u@.5D.X~5fc.#5f2d:A0Hkc 9v@bgBO-m^CoC^0H8nLfFe-r6D#H?!B/=B!Dl6cQ2#8Af=Z^mULd@B,o%m6o2Pr#FUgs#b:UOguF.JWq3_ pQ.UFzt7v?:t#04&EY.xlZk:IW$_o-IV>a/92*-J6ciE+5;u!$6ciH$4?XFz5D*p%5f2a;93dYb A0Q,mA6e,VP0BR RaI3l!-^NN&fY6YiF^>bQ&glWF;Ab!85tTD6&?Q*6cQ2$5;lv,4?O9y5fc.%5f2X&9w\m% D%vT_BqSs^D#IB6D#8?%A=1\-BqSpuA\C-wukNlHviN#QgaO?-QlNJT+KLO,q>K%ARWo$F,USMNnX#Y/|b7omt TU&XQU0hjI2#. N#H34Gd4d?O?;QoOG?.kQ@CP_NJc6&|Z|6,mDrg/*R^^$vai,~B-hytTW%.xaglSW8DmO#EVD th@9y~Ia5^;zQeowVjN5Y.%W1JUccrD%vTw92p_M85I&=6ciK/5fBj@5D.X&5fKp.6A=$#Hd0bj Pft&wP:4VXSX5PIWm_\GQBO#uOG?.dMnphALqtMENk@6KLqRUM-B:HL|FwQM+*g\FfuYaKR|Y*J32TqFf#ziJ2x!ZDk$|>DJUu$DM78Mf\6ciK*5fKs?5D.X&5fKp:6B7*%AUi#w Oh?XIJ4^~%WL\oqX#hwkQc/e/RajeITv&6BRasnGVq\;kSXWnBU1xJ*UshRJRajP7S5j11TwGLC R^^LB0MM69*GBY,iLqR,|JUu?;H8,joGcPPFDk>==DJv*0EH5uADk>-# A0H~xGBh%WEG\c5CKnbK6&|c>6&?T<6&|c=6,2ZKe3z&a_NbMG_-sMWkCCOu.>gM|V*T*:Nl^$m o=\g\yS>4|_N0eoos-&bVo*v_Jv&onBO;Xr9>>SR78Mf|6BH5$6B7>$5fKm,5fKp;5fKm;6e21~ J2WsaFF!FHTwYdOVOmvAO!1r,R^!hFWNmG0WMpJxXlZS2ZDwO-cz#I&aBFmPYHDk1WL8s8P:3:= UTSAwWOH:53K1 C-Cf;At5V2MMp:PLqR%.Urb0uKtxl3 m_6|dthc:nySAg8m5X$>R_l#ZIW^XQAmZE~mYf\TlScy;Mpb8BH@PfJQgMN3Xf Q;p*uY!9>\2=; C-Ci.At5I*L|OzNLqa/>JvcZ~LO+t+F.P+kL|FkDK0rS*Gc_0iH8L,PD#I83Eif#FDlINAE?EN3 DIzH/F;S;7D#Q-%9u.Z86&?T\5;$2*6&?Q/6c_>~YloMhue|s,zN3:UbA:PE,7#\!T1_PlK/rrLwlOsiK$Zh#6cdOhi36GA#48Bq1Fg9UL7R7^0;=5fc@!6A#@;5fTv.5fc^.5D.X&6dE8N 9UvkiBqt\!E!BYeJ>#iqFD;_CGd49gIzd80NKaK:R8v_0XliqOe=^@KbZl.CR_KuZJv>42EiEW8 Fexi9F;S;7D%>r,8x%856&?T\5;$5/6cZB+6c_$tS~K,%pAs|:d9vvJR7^0_\6B7>#5fBd~6BH8+5;&$!5fBg@6d52J 8y:7ZBPJ^~Co3@5Ff1x3BqSjsBPb\,CoV23Ej$j\OG?@iR9R,eYiVe1X%1N6Lq9z%F:4pzIWaFV J3@Zdfq|F72ll CMF\zBOxU+MM/9TL/~71IyX5^Ks:|xF.7,iKS4xCM+pV5I5IaoH_+3UEiNi7GBPkOFfuMJD#jTC F;l7HGB7SIDk@ TgQ76&|c=5;>B\6cZ8#5;>mjOkjGBo20C;p/7DdYKy:v.>y;ITTDnuML,TLOeM~E?xk|ARiweARQbU8y6B05D*g,5fcy,5EB^$6ciH*6A#.:6BQjE 9UdMYAtEIyCnYE$D%@r!BqbssASNgzBqt\~Br7gBKSD;EM-dptQczD;Qb_A6@Ds26cQ2?5fmRXLQiXjkeQsFmyLZ>Q:m48.20ouNI*e7Iyp61 p2EAOpPZVOmXLvUYFA7&WkDJmr.A0Htg9>>SU92gQ25fKp;5fc^.4?gR,5;$2-5fKp;5;$RF 9vvVeAtEIyDkUT,CnF!_DJdl:BO;RsC+/K@C-Ux/Fg89zLPSYNMMgtELO,&5KtMV.J3BKmEiNx9 H90ahKRqoa1m0 CMF>zA?=FfT7LE?Wb=9>KoA6&?T\6c?m26cP>:4?yzGK1.YJgp7=li!I9.LsE.SvXNm^HZChDDJv$V k?oaEpqi78jf8b.Sy4,sJvcEeC+/Q@At50k9v@bb8xGBGkRD#H|*BN_DG7Znl|5;>B/5;&!.4iOb5J4jk#c7ue1h$w~kOgu25yK0PilH^jvY EjBbSFEA|91yB BO-duA8*tcyMG!NJT:EIWsRSDJCHzA@JU7a1NP9w014 Wr>_Mih6QyVOdp6L/~73IWjLSCMF$vBO-OlAR_%d8W%H55fBa_4?gR~4?gO~5;$2*6B7*.5E2~2 A0HqhAtEFrBO-gxCL%poAt4N At4/hAtNCnBP&aNNk$LPKtDe|I5sytIXF8xIXX8vI5ssgG@VUrKSDn|GchwWGcz+cF;&bUHaRym Gcz,WGchnPEhr--CKweJ7Znr>6cQ2$6B7>$6A>04F.|.>Q@wJdjHA_j!o?k!HyIfk7^~_XBr7*e XMTBdY:/wlP;~urL/j%|I5RIRB/%@uAs|+hA0QqX7Zwx=5fBd_4?XI_5D.g.5;u!%5;cm~5E2z2 A<-v&C-3l/DJCW?C-Cc,AtNClA0QwgA=1!#FEB7MH^N A6&rH|6cZ8+6A>32E!$X*P.GK7ew;J5?r$&oRxckP9U$:tFgip< T4Z2dQ&z7^OiW5jL/s//HZ+IUB/~!yAs|/kARiqW7Zwx=6B7*:5D.X^5D*m?6cQ2#5fKm@5E2z3 BPS!,D#^oGEi5c4DJdx+Bqk@yBqAatCMhZ+D#aN4F.r0ZDk@&*C-L^0GBq6&?T=6&?N+5EB^*B\;P,O!A&-Z?&D8^+8dCa6$LEDK0ZLJVQuJ O?o5hL/kJMOifNqMM6F=H^RgbDJm_+Bq1RnARZhU7Z,@05;&*$5fBg&5E2v%6cZ8$6A#.:5fT:5 BqbyzDJv$1E?o&9CMhQ?CMPK#DI.\wC-3W&AtxpyDK9N4DJd&=EGsQBFEBJSG@+.wJUlivJ3Kx< M-K%EIyg2n1^9 BO-XrCL%yvBP1,5OG?pQKWoW0G~fKNq0Jv##!Ku1PK L/~3>H^~a5PEu7?N%>0BH_+9YEi5J*CL~mo9>>VT7Z,@06A=*%5fKm,5fKy%5fKp:6ciE-6B895 A=1^^B/%*~CnYB;CMYE,CMYQ%A\_eYA5EH5_KFfuYQFEBJXJv=|+K7_+@15f2d.9xy!iO!k|_O?W2^dybx$ifUF@L/k13KSM_2 IyNvdJ48;UQd3q*N%# BPSyvAto$*C+q2_Cn/f#B|7H.AsrhZAR!0lAt4N%>6HJ2WsdE?EG+Bq1Lj8yp?K5;&$!5;u$$5;&*$6A&s^4h<0w5fTy?6A=** BqkvtAt*C2DkLH_C-Ll$DJ+B3DI/5xCMP8&CM6\&EHXSmK0ZA$Iy.f%KtMx8L/~MBK0G;&Iyy2h F;~+6&|g17^0!25fKp:6,BlCPFG;BOF&0mBq#E-J3>J%DkUW@At50l At5eARashESXWn6Nku+BJ2*8kEGs7-B/JLh92plC6cP>?5fc!+5;&*#5D.U_4h\* AtogyBq#T\C+z5~C-Cf%Eio=KD%>;$DJmx-D#RE6GC4m~Kt4f2Ks|W5LP11CLO+z>Jv=?,H_me5 D#sG*B|7E:1c< BO-atA6cra17^9$05DyU@6c!c%PhDnhWK@C1ZE^plNkca!FDoe=Bqb.? H9%*OSX.6PT31~4M+yY2I5!@jDkvu%BOe- 93@/yC-Cx=Dkdf#DJm$1FD-;8DJv\0C-Lu=G&/:XH99*,K1DIpgZ7Znr>6c_j36&?N+4?XI@6A>LQLsV^Tfr/8Z&&/&!j,2EoZdOc1LqkMK QC3?5T3TCMSyoa#KtDS?GA#AIEh^4-BOVzT92._G6cG*%6A=\+6A=>+6A=\%5fBg&5fKy#5;l^% 7bGPpCnY5&C-L@2Ff&hUE?fr7EiEiCEH5?KFflbTFg7/qLO+#8Lqb77K0ZA#IyNsYDk$&!B|GB; B|Pj0Ei5f41T* Bq~\zAR_$5;lv,5E2~3G,=Kga;zG2x<-P_R_$PtM+.h4H^j\. N$o?+S65e8R^HtyKs?7,F;J_AE?NM$A08YW92*;G5;lv?6cZE|6&?T|6B82#5fKy?5D.d.6B7>+ 6,A?iDJw26EiNoCF;=bRGBGbLEiEfBFEBJSGcYqVGBhvH8nIZEiWxEFfTAIDJd!0D#sM*6ciK|6cP>?5D*p:5D*j.6B7>+ 6,A=rEG^cCEiEuJGBYtSFE1=GD#R88GcPkXH_,0SEig4ZK0G|.Gcz/gG&/wIBq1OmAR_+mCMG8+ Gc|FlGBPeF1g4 BO-OlAR_KRi7%I5^$xK0G;&Ha9yvG@C/TE?*GSFD-?ADJ,B6DJdu#CMYQ/ FfA#GFEKGPGA=SPH6~Aw7Z,!45;$8<6cQ2+6cP\.4?XL,8YVJCP:;YCmXU>ONjxqoDkUc+GCDgz L|qIkR8$,%O?4aIIy5jYEh^B1DkUHu9v@YW7^bKA6&?T|5;u$-6&|Z<6cP\:4?XFz6B82-6B85/ 6,ck+F;S?8DJe5IH^apkGA=VNEiEc8EGsQAH_v:NEG^cEE?f&GDkmr|Eh!4!At40DkdQw9v@PS7^bH96&|c<5;u$-6&?T\6A#@,4?XFy5D*j?6BHB| 6e2Y$FDWT0DJwBIH8nRmIWsjjGb=1CD#aG>E?*7MFe;o8C-3f$B/kmtCMP5xA0i%jA<;jwB/%!! JwHG*H8d$6cP\.5fm0EJV^b-hl_1tn>!2Ta&WsuPfu7| R8mt+O?xNpNI.C?FD-&8DJUo+DJdo,9vvMT7^JEB6&|Z\6crT=6&rB$6A=!.5E2j@5D.a;5;u$+ 5:5&yE?5J>DJd$AH8,gsJUBT^I4deHE?Nc1Ei.GND#IB7D#8-\B/kgnAtoaqAR? J3vH0I5saT1g0 Aq8N~!QFDrI,0yVqRrl Q@Cn\PfANlLpw7rEiEc1CnO$wBP1gq9vmGS7aAHC6cQ2#6crT=6&rE+6A=\%5fc@%6A&^?6A=*$ 5.\foGB7MIEh\oMH^s$xI5^*yIxj9ZGcPnREiWxBE?*DQFfcAJB/bmsAtEFqCMhK;E?;?FGBPwc J3mB2J>&Wk1m2 BO-jvAt50mB/<>^BPcO6IXgT@KR?V\IyN*qI5^.qG@40ZFfT1GFfS=AE?*APGBY:gIXXK^IXO5v Iy.W@JUKc^H^s$xJS|U@7^J5C85I&\5fl*-6A=\+6cP\.4i6F;8z+$6ciK|6&?N-6B82$5;u$$5;cp,6A#.? 6doxvH^9UcEig4THa9pmH8wdoF;cDSFD.1PE?WuJG@nmnGd3/TCnqg1DI/W-EGi|>HZwIkJUlx? KIXOE^Jv=-$JUBNwIXN\mH8wUiG&/~UG@3,NEH5uGF.ZCtK0Q4+K0Q4+ KS4u8L/%VJL/%VHMK$QB8XFiF7_+_=5;&>/6cQ2+5;cm~4?gR.6@NZ.Sc0e1!MLRC,ZZpNa&Nmt QcX-vM-2~CG&zbACnqK,BPk\&BOe/d9vvAN8X6iG6cQ2$6ciK\6&?N+5fKs?6A=>-5;lv:6A#.: 6dx#~IXO8vH8V0YH8+mrJ32WuEiE&LDk,28Ei5oJGdVjsI5\*oGB_FtI4+FkH^0IfH^IppIXpN^ KkdA0Q:xF.7zfIy.T_I5aakIWsUXGBq/aFflYVFEKMLD#jWAFE&+qK0P|?Jvll& K0iW4LO+?8M-L6SMLHlG8yXiD7Znr>6ciK|6ciK|5;cm&6A#.!5;$ILMQfF<_pYg~~;&KoabsIo QcOxqLq0q$Ff1_3Bqb^^CMhN@ARZeW9vv7M8W%QC6cQ8*6ciK\6cZE$5D.a,5;$2*6cG*:5fBj, 6dfrrI5!*wJ2*EoH^1*E A0HnfAs_tb9vvMX9vvGXCoeHIJw7-&GBPnWH8C~PF;=kSD#aN7D#!&IE?.1JEHgAWI65*pH^9Rf H8+vyJvulyJU&=+KQ6&|Z=6&|c>6A&v,6A&y,5fT@|El6dGo2A3Vwwj46A=\$6A=\%5D.X&6BHB*6A=*!5fKp. 6dWZkH^s*vI5\>tH9kT%JvlZrH8nCbFfcbZGcYbKEiEuKGcYhSFETtkJUlu&H^RmmH^0FZFEBGU JUu_;KR|S!1@8 9v@YfAsZPT9vmAS9vvAOASo/1Jw7-~G&/,UFfJ=GFf&hRDk>|6&|c>6cG$!5;ls&5D.d?A1p@,dxnsek@S%6SyfU- M+yY2GB7SLEGjA|BPS*@AtE6mA08bX92glI92*#J6&|Z\5;ly;5fTy?6cG$.5;$2+5;#>+6A#@: 6do&vG@wppH90ysH9bK%KRi1.IXN!iF.7:hGBh,UF.7waF;=hRFE2ARH8V3bH8nRlH_+CWEG\&N I5\$oH^9FQ1f< 9UdMdA\iMQ92.#P9UdDR8zCt;K0Z1&G@3uNjD#8#$MP6&rH*5;cp@5fc!$6cG*.5fl*+5;$2+6A#@. 6do_rFEuzcH99^pH90.^KRY#;JUlryG@DIjG@,vlFflbaH8nOjIyg5qGcYtSD#RKGH7zeHCn/@B Jv&lvHY/P91Z* 933DeAsieX92p_P9v@PS866|qK0Y|&HZ,RjI5adhGB7qbG&hJCDK0ZGGBPhOGBPwZH8UB|6B7>$5;cm& 6dorlFEBJVGd46XE!2JVIyNyiH^j^nF;#ncH^^*pF;uYZH^RahJ3T;%HZU/XE?WZ2EiExCCnzpD L/~A5J>b$T1W$ 9UdSg9>>VW8yOxS9v@PT8XX=mJ3c_.I5s!yJv#&@IW\$sGBPkNEHN|OGA=MLG@C/WIypBwKR?V= KR?f6Mn.+TM+*:ONl8E$7_+@278Mm06ciB%6BQK<6&?Q/5D*m?6B7\?5;$92BrQ5WJ2.EqFflDE DJdx$B/=2;CMhZ%A6&?T\5;cm& 6d5NkD%vf#D%$c_Bqk!&CM7K|Cn/u78Mi=6ciE+6ciK|6&?N+5DyO_5fKm@5E2p:6c_j9A0QzlBPS$@ BOxFkAR!0oBOo6jARiwd932|Q85~lSBqJXmA08hX6&?T\5;l^%6BHB-5;&\*6&|g16&|c=5;&.: 6c|>WDJCi/DkmWzCMG5&B/&B*CMhK&B/%>&B/%!vAR^9xCLkRqF;k?6EG!W1BOxFmBOxX&C-e57 H~X$6L/a~-1*E 9UdGV93LPdBqSdj9UdJU85tWNA=lyNI5jjiI5\>rGchnODk@#\B|7Q|E?oxBEG^fMHZU~OF;uAG FEB7SI6OZ%L/6ciK*6&|c=6&|Z<6A#..5EBy:4?gO^5fT@+7^SNLA0Qzh A0Htg9v@Va9vK?PA08kc9331Q85%xYBq1Ii9v@SU78Dc<5fKp:5;&$!5fc!+6&|c>6&|Z\6A#@. 6c?!PCMYQ+C-3Z,B/~.^CnYB.BqSgpA<-mvA=1^w9v>,vCm#iu1.C 92.?Q8y:DeBqJRg8yp#S92giKAuKmJH8wUiH8nIcFE21KEG^T3B|Gd1GBYtVGBP:nJvKHvIyW$p IzB_+K<,-ANl8gePD,>S8x~246&|c>5;#>$6ciN\6cZ8-6cZE+5D.U_4i63w4?XO;7a18G85c#PB|YyFIXXHyIXF2xIyyNxHa0XgH8nXpIzB&*KtV;ENJK,IL/2/6cQ8/6cZE*5DyOy4h\DJLo-B/~\&BPAjsA0HwjCMhH&As?zb9v@SW8W%TIA0jCzDKIiE DkUQ&DJ3$M2BN 93CAS866&U9vvGU9UL1TA08baC-U\FIzB;|KtDf1Lqk74K0ZD*K0ZJDJ,86D#R81DJv>4C+q8^BP1gsARizhBPk.wARZeX9v@SX92*=R9UvwoASEs! C+z8&B/l9722M 86F;R85tWL9>@DS92*-SAnfBPb>:A!5DyR@ 6@D\PCM6\@DJdl;BqJmxCn/f$C-Lx-CnzW%B/%\@CM7K$A<;aq9v>nc9wHzf9UmegAsZVWAt;y@ DKIcFEGaKE2BZ 85~WG932|O8XF_S92.-S9vm7Q92yxbH8wXlHa0dmG&/tlARr+nALPAAHN#r.iOH58p N#s2^R837xN#ZvfNl8aWM+;$5;u@.5D*a^4?O6v4?.p?7&n/MI5I9M CnhE;CnF@sARr+mARs0tBqb.~CMG5?D#H;?As|:c77|K$5;$2+5;&*$5;$2-6ciK/5;&*+6&|W| 6c?!LAS5d,CMF\_BqJv&CnF^rAt56nAtWLnA0s3sC+/j1G&h73BOo0eA0QzoBO;XvDk>>2FfcJK EG\l7EHgAQ2EZ 9v$hiBO;Ll9vvGS8yy#P9UL4S9UmbrH8wanHa9prJUu-*K<+=7KSD#GLqtYKMn/0VN#ivdNJvRZ Nl8pkPft_wPD-KpPD;8gNk&!:6&?W<5;>E=78Mc\6ciE-6A#..5D.U_4h\XZ NlHvkQBqM:PD;EmOh|^eN#QR46&?W<5;>I06&|Z<6&?T<6cG$.5DyOy4h<0w5D.U^5:XQ1KtDY% GBGeOEG^J=B/bpwBO-dyC-Lx|E?*7NGBq,PCm6&?T|5fKp:5D.X&6B82$6A=$? 6BHUKDl9KDH^j@sKSM|MQ@Un?MnXS8L|OzKKR_G/LP9=8LOne\KR|h>JU$4*K0?r9Nl8aUKQ=is FfJ-EG@ep~2x^ Ei*beIX5$pHZw3UDkdZ@CMzy7IzK;$Jv#?%K<,-9MMy6&?T<6&|c>6cP>?5D.X~5fTy%6B82- 6A>35Cn:yAJUlu:L|6tXOiD*fLP0#4Mo36TLPA1DN#!T%PDMgLKS4u7Ktw=BK=SqdVq^oeQ&/7l IXE>vJ3Bl.2o~ I6FQ,J2*QvI5\\rFDoo9Ff&whJwHA/KRrD*KtMo3K<-AGLqkJDL/%SGL/%SHMn..vN#ryX K|A=A>OJU@4|Ku1SRPf#N4Tuw/@Nl8RH F.Y6crW>6&|T|5;&\+5;u@:5fKs?5;ly?6A=>$5;$jlNJ>ma Lqb44JUTcwF;J;9C-CrCGcq:fKSDx6LP9-0H83,K8x78Vo=6A#@; 5fc.#6dD*D7Zw*A7Z+>B9UdPYAtNa%IXpZ%MovykN#QaUKQ=i~K<,?0JV8A$IyW*hE?fo7D%dHz G@e6QDJ@|\1T# Jv==+KtMn>Kt4VK0Q4$KtMr6MMXqCFe;lDIypE_KSDk;CnP5|F.QF;Q@dh^Mo3CZ M-UObOG|\nOiWKzRYWQ,Iz$Y;7^J886&|f>6&|d06cZE|6cP>#6B82-5;u@?5fKp.5;$ORKSxSR M-L3RLqb44I50FZD#soMH8,y~LPbSIMnytDIyEvV9>cxA6&|g37_+^05;$5/6ciK\7^S225fKm@ 5fT^?6BQR47Zw@378Vs178e@68W&ZiBpwzN6ciN>7Znl\5;$5|6&?T<7^bB45fc.? 5D*g,5;>L27Z,.26&|c>6@Dv57Z,\JDltAsML8-aAs?/uH9~vCRa!zCQBOraJUl&.J3BNhB|PT| J2x\gDJCQ~1N% I6Xi+K7Zw^27Zw^26@4s46cQ8/6&rH|6&?T\6&|Z|5;u$-8Xh4mIXpl\ OHNHnNk@FTK<4G$Iy*i:K0?xCM-dRbN#ivaKB\6&?T<7^b836A=$? 5fKm@5;>L178Vs06crT>7Z-8F8W~NJB|Y@HH_C6!6crU2B\w=oOI2JjEjmC_Fe;V/D#sZ5CMG2v85kB77Z,!46&|c>6cQ2+5;$5|6ciK|6&|Z<6&|mAAtfj?H8+^: OHNKrN#QdWLO+#3JwQQ0LPSPMN#i@kO?;NlLq0h^Bq1Od6cQ8/78Ml>6&|g278Dc=8WJR>6|F;t|G H_mwEDJv_:1N% JwZZ2LPJ79LPbPHMnyzKM-2%OL/%5;u$+5;&\*78Mm18y:DfDljrIGde!+ N#!2oN#iybLPA49LPJJJM-dRdO?;WvP.6-yM+yU|B/bUc6&?W=6@4p378Mp57Zem08W%E36A#.! 6B7\:4?pg%78Vv378n$C8yXlI8y6QHAs85kE97Zw.56&|c>6cP>%6BHB*5;$2|78MsBBql2*Gdn^wH~6^B OifKqOifNpNJd9SMMX:ZO?;WvP.GA?R8@:-OGQFIHzFGt6&|c>6&|g26&|j57Znv28Wt215D*g. 6cP>:4?gX?78Mp27Z-2E8W%QD8X6oO9UdJR7^9\578Vs178xEQB&NiD#8#+1Z| Ktx7GMn.~IMMp,MMn.:KL/izARizkCMF!q92gcA7^0$678Mm05;u!#5;u$+5;$5|7aJcXEHOJkMn*+LK6&?X06crmB7Znv285R*05D*g. 6cZ5!4?gU.6@4j078w*76&|g17aAQM8ypxI85kE878ey27^~xXC/70|OhrIDI5\*eCnzj2EF$@A K0Y_tE?WV|1Q# KSV=GMny~NMMp%QNJvLTMo30RM-T/CJvT5lJ3TrxF.GtRDI:B.Dk@x+C-wcQGb~,-6@4j3A=cH=G,1E>TU&32NJvm@ VOLjHQcz7@RZve,O!1x:T3cRUU<+WiU=0fjT2xX+KPVs+78Vs06A=>*6crmB8Wj>57^9*15fKp: 6cZ5!5D*m!6@Ds26@Ds26&|j37^~oN9UUDWA0Qtc8ypuIAtEI^I6/KINl8gXJ259DD#jQ5BquI5 K0P-;F;Sq-1E: K0?uAMMFeJMn.%OM-B+KK=SePOG|d9GB7wfJU&&yH8e3QB/ks_EG^G=C-wWNH7hA2At5CvBq1aw C-U;=CMG5?CnO*xBOxCmC-CNu8W%QF8W~H96&|c>6cQ2$6crT=7a1KWF.YQczZ2 VOv@KR8UP?S5r<+Q@+SJV|gV.X#_O/X#|h0WnNfIJt|y=7^0!06BHB/6BHL37_+.47Zw^15;l^! 6cP>?5D*m?6c_p87aABA6&|m68y:1YBPJ.:D#jW6Cnh2_DJw5HKt-edS6EnBKqwy>Eifx1A=csM J2.NzGAkq#15~ IX.&\LqkMGL/NI;/~Ff>9xK0Y-yH8LwGB|78;E?--8C-+WEGb<=3As\3vDJCT; BqJv~CnqK:C+:Q,ARiwfBqSaj78Vy68X6fE78e^56cQ2$6&|j59Uv~:IyN\xLrG6uaesbtW+Num Xl?d:RZ$t$T3cCGSz&*tY!SRKac*@LZ.g$WZe>/YJ=V<392gfA6crT<5;$I68Wj>67^0!26A=*% 5;u!?5D.a,5;>XFANl!y4UR6@*J0c~?DljD+B\DlX HZnCcD#H-!15y F.rRzL|6nMMMg,NKs_7?IyyT^J2NUWH90$,J>=frG&zeFDJm;/Dlsr8DKIQ6GA#16A7Z,$67Z,!66&rH/6@D^FBr_HRIygE,NlaO3a,^h:a&60D czkzgU0YUGU=5;$RC8x%578x!=>6BHB* 5;&!:5D.X,6BHUMEHW=IEi*PRFf#%qL|X:RNlHgZNlQ$sQB/&9TxfA6 FfJ-DDJCW,1E& Dla&aL/ucyIW!yjEGa88G@njvIyEvhGcYbGDJm&/D$E#BDljZ6Ff1_4CL%.~CnY5; C+X!zCnzW+Ehi?.A08hc85kHC6&?W>78Vv37Z,!56&?W=7^krgJ48Z1KtV#LR9t0lbbW:=fOdL$ gnD.zZfas%W*Z/Sc6xq.fPjF0eS3O~b^|=nb7ojhNIWGS8X6ZA6crT<5;$RC7_,v58x!=>5;u.# 6cG$;4?gO~5;u*=ASy2/D$6DdIXpv0PE#P\T4G@aWoT:$Vq^;dOix=$KQJdGACJwHN4MMXwHI5jjhGcqzOCn:s7H8VIlG&qePF;b=9B/~*:GBqzKEHEo7Eh\P=B/%\;D#8;- B/JawCnzN-GAk~>ARZnb92gcB78Mm07Z,$878e^36ciO08z3w/MM/9WN#s5+Yi-3IeS+aOiGzcO hkt^1b^_lRXmERkgouiYiiw7Vf|EN|cXf4jZ,@zXK|b2*78x295;u$#6cryG7_+@88Wt525;u.! 6A&v@5fKp;5fTy%6dN8HA0sX\Ha9!-WMo|vXl?:Oc5|oMSx|tnH^IvuFDoY\B|7X4H7zJ9EHo|E FeD?%DJdu-1f= Dk$#>G@w@;L|6eAG@MCcG@C6cQC0AT2vXM-@^tSzKgtdwPC-hm4Stkc;%h iH3rJdwO/vb9aA.jFOg$l8=vwhJ&26cy;4bXI;fGIwBz;78-HF6&rB-6@E2N85I*A8Wt535;u$$ 5fBd&5;&\$5;&$%5.8R38yz4cBP1,7R^R1Ueu0F4c5Z57OhP.1CMPN>Ha0IXH^IUXDJLc!CL%vvE?*4F Bq1XvBO-p?EF>o&ARita92ylB78e^36@M!86&?T<6cQF0C/F~^OHyHKZ:L#.h#:|hl9QI0mXnZ$ kBo:be=I61frN$Sl9_sBnU%4=iH3rGcyw#TV*$f/ 5;ly?6BHB/6B7>#6cra38yp;O7^JHRI6Fjqn3^;rZDU,lMK$=pEGaNEGAk-DFfunaH7/hIEG^H1 Eg>f#CoL\61m4 GcPeOD#!uKHaIyqG&qbKFEcVKDl0NGGBGYNFflPLD#^rIEiN;PIX5!qIyyHkC+:N.B/bgyF;=eL BqAasBP1v!Ehi#:AR_za92gZA78e@46@M!76cQ5-6&|pEHbO.5Rby\?d4_IRk@u!/n3O2IhLe+;n46uSprE0nrKP8@ pq?hMkdKg.m6n^7o1USgqM;6cmXM5ueROPNTT-UwFgG+c1.A H8L%VG@C+UE?EZ3CMGE+E!BPQF;#eUFDx&JH8wOcFflwoIWsmuKRP;xF.7tYEhQx!CnO>:F;#MF CM6>&Bqk*:CM6*xAt4,Y8yFT978Vy87Zw^15.8dC92q4;PilL9hK?JijFXv>oS@hiqok^~sHv&/ r#y$nmXnf~%1NH_lC9UvVV6&?T\6@D*C6cQ5*6cP>!5fT@+ 6&|Z/4?XI&5fKm&4h<0y6crd78X6iJ9U@<=Pho9mTwru6@oEJ867Shb6&?W=6@4^66cQ5-5;ly?6B82- 5;>8+4?gX.5;&!.4h<0y6crX393C7T9v>z,OJ8tsVQO&8ba7=\PE1csOHNEkML%11KRh=%K=ADI OG|.dNJl:EHN#H EF@Z;Bqb!:D%$x#CLS9c8W%N978Vv36&?T=7aANH8zMVMYJ_T8k@up;nw:4dr$Lr\tE!Q5t.;?E t.fV*otv7OnVX?Zr$Lo*sHCBuo0yV5.8a35;l@%5;ly:6B82+ 5;u@:5D.a@6ciH+5D.X&6BQR49vvJWARr+^LrzmpP:MPOX$;UMZgFvKUt,fpWKmO7QcFcdL|X/Y M-2/PKsPWq1!4 GBGhTG&/~TH8C+VEGj52Ff#wgJ2x\fDk+27GcYM7C*0?aGcY,iH^asdD#^l9CnzQ?CM6*^D#jN5 EF$c@BPA!+Ehr|#A<4qV8W~E878V^56ciK=8XO;S9V~LZZikDElah\*ouH:ts!sT9uCA|GudlGK tg5J/pPZbXoSmShsHv&\sHC8soS2e>gnMyjT>tm#LOD1l85kTK7_+_=5.8X25;ly?5;u!%6BH5$ 5fBg&5DyO_6ciE+6A#$!5fm969vvGVAtfp%JVHW4MNn8~Pf=G%QdCn%OG|*jNJdXgM+yOfDlIiQHZm~FEi*nkGcYqZHaImbDlIWBDk@&*CnY2&D#H>3 E:_#%BPc8|E:\4$AsrbS7_+@37Zw!66ciO28yXuRCo,&vdy~ckmYSEDprxp.t.;#IudlDJuCA|F tg5J|pq|~epP?r#y*pnwOG\hJA5kT2o6_J2EaL85JBL7Znv05.8U26cG$!5;&!!6BHB- 5DyR^5DyLz5;#\$5;u!!5E2y=9v@MWBq=Q\GBYtTFEBPVEi5lJH^aUWE?*7JEGsoNH^j@vOHM!W NJvOND#aT71N! F.7qXG@3/ZGcYqZG@C,KDJ+WPJ2*5jE!BqmH^9IWGd4ayIyE^kHaIjbDlIZGEGsDr#DlIH2D%>&$AsZPR7_+.37Z+\778Mp685tcWG@e:GfsmAyourp|W0jprNFwsi>#\r#gvjnU^#+hJJKsTvSU_H_vq37_-2K7Znv06@4s46c7..6cZ8$6BHB* 5fBj,5fBg@6B85-5;u.?4?pg<9vvDRAR?/jBP1dqA88XFuM85%xgGe1^ojhmmIrKhN%s!\_MvaztRudl7Es!aB4 uCA-9qoALlprNFwsj8||q-bLcmXnZ?hJko&U\/2:I4@a-78MvD7Znv06@4s35;Tg&6ciE+5;l^: 5;&\#5fT@$5;u$$6A=\#5D.a:78n$99U$,nAs|+yKRYijI^5?Ksq&wI5!vcD#jlJEGZ-#B/%*.Eh\T7 Eh!D>D#^iAC+p>r85b8C85b5678Vv79334U86PGoHbz=*keZ@LrKzp1u,GqXv~3~Sudb=Cs!jQA u@#JErJtXlprWLvsHmr!p|DqSm5|2~hJbc~U\p.,IV$F?6&|d478Dc<6&|m25;Tj;5;u!%5;cp@ 6BH8-6crX06&|Z\6BHB/5fBg@5;$2/8_9U:ARi:+MMW/^L0MT.Kr=\PUs6FSBr7^NLqkJAF;cMb H_,jzI503c22K E?*JYH8L%UGcz%cH8U,KDJ@->J4HuEL|O~PLqa<|HatK%J>=oyIX5.kEG^fDDkUW?D#a1*EG^T8 FDxr9FE1-EDI.!f78n!A7^9\56&|d49Uvhb7~GYyI_a|Al~oBRrK:y5v9q:av~L/Vudl7EtF5oE udl7Er#Xvop|;dyrlq8zpPQJKl8&dpgMM\uT>ba~H65;ly.5fKs?5;cp@ 5;u$+78-KI7_+_=6B85*5;ls&4?XI^6@WBOA08q.L/94mJWW~iL*wc6Nkc~CIyXQ-Nklz6H~6y8 Juy8:LP0-82We Eig4WHZm/SFEKMSGcYPDD#jT8IZ0AcPD;5fNJc+DJ3vA=JvcNuIXO8uGA#ACD#sZBFDxi0DlalJ FfA;EGchhJCLb9U78n!77Z,$56&|d49wa0i7~GMtK=&qKmz<#gsH+8Av~V9dw6n3Xu@=SKtgWuE udS#9r#y>yq-=2:rK6^roSBxBkBo:cetL9iSx_zuEgcyk8W~HF8x%536cQE=6cP>!5D*m!5;u.! 6c_g692*#L6&?N+5;&\+6Aumz4h\R8CJ*U6@4yCAR?TuB6&rN>6cP>!5fT@$6ciO0 85tQC78Vv26cQ2%5fKp;5f2Rw4Gj~r4iFI,7aJTPBP&>hPgq|GaDIY*hlPZDcXWY.czJSmd2+=g e<>^^YjbH|2&v F.7kRFfcAJGBPtXG@3:NDJv#uZyIyyQsDkvx*Ff=VFDJw25Gcz~W E?EW9Feo7^7_+@58W~B87Znv06@f5LBP1auEhQr=OHXy2pP_:ns!^iIw6+akw6wIdv9PYJs!R20 tgNf6s!H#-sj8=>sHC8to0.l6jE94MdUbGSS5ZtvFCrox9UdDU8x%856crW>6cP>!5;$5<7Z-2C 78Mi=6ciK|6crQ\6cQ2#5;cjz4Gj~q4i66z6cra28WQA~TeKs|7,J3Br:H_dqMFfJ?8Ei5Z7FflPQ EGQ>0DIgme8W~HB8W~E97Zw^16@f8PBPS^.HYg;2NNA0lpr4+nsjacIw6+Xhw6n9bvaqeIsj8>1 t.;?EtE^D>sj8|IQNk@&RY dw::3c7b%Y2_w FD;_EEi5fAGBh:VDkvu#Cn:c$CnYa4EF>\TQBhA.R^8t;NJBqDMMXtLJ>J\gD%$u+EiEf5D#!_K D%>?-AR8DN8y6ND8X6TA78Mi=78xBPC-wTMF,o85T^lcbpQ59ssjRTGw6wIev~3,Uu@&4Bsj923 udlGKtEs7=sHmu-rKF.totv1GkBNnWeRpqSR_$VqE.>2l9vv4J6&|c=6cP>%5;$5|78Mm06&|d0 7^<|aB/=B%DI.*o85R\46&|N!4?yg@4h<0w4h\78xETF.Z9nFf=/$grB6Jr$Cf$sjIHBv~M3bv9PeOt.ff3s!jN9 udlGKtEsA=rlzK;q-~vspPZVOkc-RgR^8qsEFT.l9T*uE6&|c=6A#@.6BQN=7ZnyBA=1.& B|PW:Bq1Om9v2lA6cQ8*5;u^@4i66x4?XF^5DpCv4?.s#6&|d285~fOBP>BdKRY;yFe;S-H&nJk OiWpDX%#1L2BX EG^Q4D#H||EiNo8DJv?-CnqK.CMPB?C+z8%JU@HCQde46QA~TcKaHD#R21FE1?0 A0Z~c85tcL85R\46&|Z<6ciB!4h<0x5DyR~6B7!&4i69^5;u.#6crU19waR*HajvbEh_|!BsHLQ&qobpvp|e34?XFz5D.X&6B7$@4Gj,u6A=*#6cQ8/7a1QVCn/s1FfJ?4DK%Yq JT5v.LpCrj1W\ FflAEEi5Z4EiNi3CnhH?C-Ci%CM6\@B/tmoARr,iDLY3|Oh?XOMnyn6IXO5sFfK4MGB7hSEGjA/ 85S2C8yXlG7Zw^278Vs16ciN>9w^~BLr|Q:OmKygnwOrZs!a7>tF5oEu@#MMuduMLtE^H2tgf?M v~3~Qtg5Q2sivi.q-%.yqM;6cm5:.sgL?gdR^i,gBOe,X7^J576cQ2+5;>aJDl981BqAdsA0Hzt H^ILUFEcndHa0LQAs|:Y78Mi<4?XI_4?XI^5fcv&4h!%t6BQK<6ciN=78Ms67aJulFfS=DEG!iD C+YaGH_v~D1K# F;=SJEGsN5FflAEC+/Z*E?fe>C+/T?Aiz 7Zw.98W%WH7Zem07^9*46&|m9AucySOl6LaTW.V&rJ%v_tE!Q4tE\cAudlDKudlAHtE^K5ud&bV w6n3Vt.ol5si$x%rKP2&qoAFem5:@peQ\&6T2n:S9UUGX7^0!36&_H/79JxfDI*>m8yg_SBQH5Y M+W+;Jv=?yCnO!m78Ml>5;&.,4h\\3va/+a w6wFaudS-As!Q||sHmu-rK6ynn2+Ercx:_%R_cc3AiEL/kACL/~JCKsz|&H8eIcEg#\f 6&?W=78Mm07Z+>B78Mi>78Mv9BsWG>d3%/mWQV4wtgf_Fu,GnRtEi-/sj024si@o?rKqZ\v9q:a w6wIdv9GPHtE^H0sj8==sHLKxnv!x_cWhu?Og=d-EF$Km78e.A8yXrJ78Vv26&|pFF:fF2OGrgJ C+Opd6crQ\5;u^,4?XIz4h!:p3#9km4Gs/x4?XI_4?O3t4h<6@6&|j592yuF6&?T\78Vs7A0sL, E!$d/N#HXb2;@ E?-|EDJdr#C-wB8FfJ-DFD;x7C+z2vBOxIpCL<.wAs|,eFF_psFE2GfL/%YOPe,vLJUBEcBN?PK 6&_N\6&|Z=78Vs16ciK<85S2EC*%&9gn-r>f1RwZu,7bPv9q,Us?>u$rK6\,q-kXkproj.u,7kW wYIgiv~3~Qt.ol5si>-$5;cm_4Gs%t4h!:p3#Iqn4Gs/w5DyOz4Gj~q4Gj:w6BHF08XOxK78Ml>6&?T<7bHAL OI2&RVOUX82;& B|PZ/C+:Q!Dl057FD;;DDk@_+Cng\sAR!3tCnF.wBOVzUC*aV79AorK=1AELp@-X6&|Z| 5;&$%5fc@?5DyLw3#Itp4Gatn4Gj~q4G~0^5fBg~4Gj~q4Gj,s4?pg%7aJQJ7Zny36&rH*6&\N; OIvhwU>JVCn/N~B/SIdA=BCJOH5BoL*>#Q7Zns0 6&?N-5;u$-6&?K!4Gs<&78n\FAv8iza.@$%sI9NDv~M0avaqbDqoSgqnUt28m6VvApQo.=w6wLh x3\!qwzaggv9GSKuCA-Ds!Q-#pPG:-du@-~OG7y;8yOuT9>l?F6crXAG@,y_K,CL~moAtWRr9T*_J8yg#aCm>PTA0Q~&LP~zTL*dB46&|Z= 6&?W=6crT=6&?K!4i6I.78e$E87MzdbCIT=tF5lCu,PwYv^zM4o<>GMkdKm,kCB:1q*z*Hwzjvo xVX5ux3\.mv~3~Su@=SKtE^7-ot2D,dTwG%PDL$zBqt^r7_+^07at;kIyy8eB/kdd6&?K#4?O3u 5;&@@4h\tkA$MXA0Hnb8yg_RA6&?K%5EK!|9>>JO7^ScwRC0?ypr-^:s!scGv970\mXnc@e1C%Al9?^MsI^|WxVX8w xw*W^y1BTwwzRXdv9PbLtEr|.my,f*j;8MnL*wAwDjyXU78-TSC-Ci,78Ml>5;ls~4Gj~q4h!%r 4Gs%s4Gato4Gj~q4h!:o3#Itp4Gaql3#Itp4Gj~q4Gj~q4Gj~q3#9qu6&|c>6c7@.6crX185~WF 8yglB6,2N83W8 D#aH2CnqK:C+z5xAtELvCMG8.B/tsuB/tyuA08YYA0HhZ8y:1XA6cP\:5;$C2A08GL7^JBTM|3e%l&c1ntE\lHu@t:|mXnHkbA5x3nVg=guCuqexw.Ny y1To,ySll_x3!viv9GPGs!H_&nVFcEjdN8.JUTZi9>TuB8z3MeA<4qP5;lv,4?XCv4Gawp4h\/s 4Gj,r4Gato4Gj~q4Gjzn3#Itp4GRhi3k,en3#9kn4Gj~q4Gj~q3#Izw6crT=6cP\!6ciK\78n!9 85C-3c#CMF>_Bqb!.CnzW%CnqE&CMF\xAs|%hAR!0lA6c7@,6cra49vv1I7aAHHEk\*xg/oSCtgf;IuB;e*mX3gKYkrH8oT8/zv9z0Dk@!0F;=~pMMzFgPDw.MG&O%;6cG$.6B7>+ 6cQ8*6ciN<5f2X@6&|g39UB#M85~ZG86_GPWqgX5rmU*4si$_:l_tg\XMd2Eq*GN\vb43fx4F8z yu7-!yt=_&xVN!iuB;h>sHdTxpP|,GV-EwUDj*!l85% 5;&\-6ciN<5DpI^7Z+>A92*=S85tZJ7^JTcJxpDAo290vrJ%yuifvn2WPOXBr+RoJw6wIfwYa.x yu7-!ySur~xVN!htEi-+sHC2trlNmVOF&Lt9>$MT7Z,$55;u@:5fBd_4Gjzo3#Iwr4Gjzo3#9hk 3k,bl3#9km3kwSj3#9hk3#9no3#9hj3kwVk3#0bj3knMh3#Iqm3k,hv6cQ8|7^0\97_+@16ciK| 6ciN=7^bWL0.i B/=5.Cn:f$B/%\@CMG8,CnqT|G@eUkIy.NrGBGqaJUu&.KR|c0L/aq^D%vQm6&?Q*6ciH$5fT^! 5;$5|6&|Z\4?F3z78e$D8y:Ge9T*-T85%xUA1F6PS^XV@kdd30aZE\5W/.jDthTqdx3\@mwzs!w ySu_:ySur~xVN!er#zE.r#p.yriV\OJ=e$<92*-M78DW*5fKj~4?XCv4Gj~q4Gj~q3#0bj3kwSh 3k,bl3#9kl3JVJi3#9hk3#Itp4Gjzn3kwVk3#9kl3knJg4Gj~p3kwYp5;u.$85kEA78e^478Mm0 6&|c>78n\D0*p B|PW$DJv->CM6\@CnhE;B|GmCH^RaZFDxf0Dkvu-E?;|HFE1?BEGi-!ARZSK6cQ2%5;#$.5EBy% 6ciN<6&?K%4h<3~78VyA93CVmAsZYX9UmklAR_/sHb7KmWOILaQb0.oXnKpEwYa^rxVX2sx3\$t xw*Z@yt=;;xVE^ZqM;IpqMxLuoMJ@ZEFBpg92XW96cG*!6A&v~4h\=>Dk@x!BPS$.DJUc.FEA|BC+z5_A>PTA0iPaj&iFjE:ZQn7ZMZ\6@Dp05DpFw4Gs%t4h\@7Q8yp?O 7^A8E8XOuN0\p BPAvzARQeeCnhB@B/&00I4$h2A#5DyLy5fT^?5fcy; 5EBs?4?XR;5;$5\7^>GK6ciK\7^I>34?O3u4i6I@4?O3s3#9hk3JMAe2nPrW3k,bm 4Gj~q4h\m7^STQ9>l|M93LJP5f2X^5DyO_5;#\%5EBp@ 4?gO_4h<3^5;$B=93dkjCMzo/B|7E@CMYN,ARiziBOe/b9UUAR8yXiD6&|p7A0^w7KSfAOR9S6; hm-nErlz8&rly$Yih*cKKPw$>8X6T86crT=6ciH$4h!+t4i69y4h^~o3kwPg3JVJg3JD4d3#Iwq 4Gj,r4h\$DO85I0AtWUuBPk>@BPS!_CnqN#Ei5S\AR8GO8XO_P92.?N8yXrK85tWL8y/JfCn|5T Lq<%gX+t^Qgno2oVopFWB*w~S6&|c>78Mi=5;lv@4?O6v4h\F85tTJ9UvSW92/1X8yg_L7^SHI 9>>PVD$pPSJ3KlyGbbVy6@4j06ciQ=6&?N-5fKp:5;KUw4Gj~p3#0bj3kwPf3JVMj4Gj~q4i66w 4Gt0x4Gs%t4h!:p4Gs:q3JVMj3#9hk3#Itp3#9kn4Gj~p3#0el3#9nq5fc.%5fT@$6&|d17^bQI 8xhcAs?,jA=GBPYDCMG5@C-Cy1F;#A4AKf14iXa;5fKm,5;#>+6A#.: 5fBd_4?*^-78xKRAR\ItBqJaoBqJgqA0HtkB/%*,CLkLhA0sCqA08PQ9UdPbAs|%jBqSjo9w8wg A0QnX8XFuO8yFWC6&?W=6ciO06&?N+5fT@+6&|Z+4GRhh3JVJh3#9kl3JMDh4Gj~r5EBv!6b=v_ 4Gs%t4?.p;4h!%q4Gj~o3JVMj3#0bk4Gj,r3#9km4Gatm3kwVk3#Iwt5fTv;4?gU;5;lv;5fKs! 6&?X292/4Z15v 78V*MBqt$^CL<.xB/t@_A|=Eig1RH^%5xJUTNjD%>u.9>Kf14iFO;5fBg@6BH2%6B7*. 4?XFx4?ym#7aJiTAtWLpBq1OsC+z8x8yzAjC+:K;Cm+6b=sy 4Gs%t5;&.;4h\/r4Gjzn3k,bk3kwSj4Gj~q3#9km4Gjzn3kwVk4Gj,u5fKp,4?XO;6cP>!5fBg@ 5fc!-7^txX12q 78VyBBPS.,DJLf?Cn/u>EiEZ1C+/T?DJ+NGI6FH~KS4xAM-UFPFc=mN4iFL,5fBd&6A#@,5D*a_ 4h\85;u.$7Z,@16A&v@6BQK|5Dp9q2nY_Y2+|4g4Gjzn2+|7i4h\+6crmF15u 7Z+>AA0\RzDJUi%Eio=LF;#YQFDoc0Co3!7GBqA6&|c>5;u*|7Zno\5fBg;6A=*?4h^zk2+|4f3JVMj4Gatm3kwVl4h!+t5E2y#5Dg3s 4i63v5EBm~4h\/r3k,Yj3k,bj2+|4f3k,bl3#9kn4?X9t3kwVk3#Iww6A=\$5D.g?78Mm27Zno< 6&?T\6BHE=0!p 8yXoH9U>:nAqf9>KlA6cQ2$6&|f>6ciK|5;ly.4?O6u3#0Yh3k,bl3#Itp3#9no4Gs%u4h<0x5;$5+4h!:q 4h\kdA0QzkB/%!zB/~*.C+/Z$CnY8,C+:Ex77|K-6A&v&4?gLy4Gj:u4?XFy 4?gR;6ciN<7^tlS9vK-P9U>,nAsrhU8yp-R7^SEG9ULDfC-3Q@EFmZ!Dk@r;BO;m@A6A#$#5;$2*6crT<6A#@,4h\/r3knDc3k,bl3#Itp4Gj~q4Gs+v4?XL,6cQ2!4?EwBOf9tA*78Mi<6A&v~4Gj~p3JC=a3JeSk3#9no4Gj~q4G^DJ5fKv%5;u$#5;>H=6&?N$4?O3s3#9km3knGd3JeSk3#9no4Gawq4i66w4?gU.6cZ2:5;Tdz 4h!%s5fT@*9v@4G6&?K#5D.X~5DyLx4?gLz5EBy!5DpCu4Gaqj2+:|d3#Itp4h\>JTASNOpARiwf9UC4XBp;3g9T*#T 85b225D*p#6cZE/6@M^35;&!.4h!:p3#9no3#9hj3k,em3#9no4Gj~r4?XFx4?.p!6B7*.4?XCw 4h!+v6c_j88y6H56&?N+5fKp;5f2Uy4?*y!6A=$:4?Em1TASNOqA$MR85~fN92yxJ9vvDR8yOiI85tND7^JEE85c|S7^12C 6&rH-6crQ/5;$5|5;u@:4?XIz4h^zm3#Itp4Gjzn3k,eo4Gj,s4h<0w5DyLx4iFI~5D*g,6A#$# 5D*p*8yFT96ciN=6&|Z<6A=*$6B7*.4?gO^5D.X_4GRkl3#0Yf2L#ZT2+|1c3k,eo4?yz6AR_/o As?+oC$^/K0mZ 9UB?I78n$D9>l|K85~ZI85tND92*#M92yxM8X6iH7^J8C85~WD6c7^&4?pU~5D.a&4?O6v4?XI_ 5D*g.5fBm!85bHF8X6ZG8yOoK85kEE9T*oE78Mp16&?dCAs_~gA0^3qAs?,e7^SWN85kTI6&|j1 5;u.!6&|W*5;>B|5fBj,4?XFy4h^zm3#Ito3#9kl3k+nq4h\6ciR79ULATA0r%5;u!?4?gX:4?O6u4Gaqm3#Iqn3kwSi3k+nq4?O6v4i66x4?O6v4?XCv4Gs+x5fBg@ 6BQQ>6crT>7Znr>5;&$%5fKp;5;u!#5;ly;4?O0q3J3-a3kwSg2nGiT2nh?a3JVPl4Gt0+ARZtj BqAj^C$9bA0mY 9UUDR85~ZG8XO&O7^bTG8X6fG8WL37Zei\5;u.%6cQ2#6A=\#5;u!#4?E+m2+z&X3k,bj2+z;Y2+|1d3JVMk3#9w_92*-S ARr/sAr#x50sc 92.;M8X6cF9UdMV7^JBD85~ZI8X6lL8W5;&*%5fc.?4?XFy4?XCx4?gX. 6A#@.6BHE\6@4m06ciU585~ZE78Mm48W%N96ciB%5E2y/78e@68X6oN7^JHD7^kWE7Z,!46cQ2# 5;u$#5;&$#5DyOz4?XIz4h^~o3#9km3#9kl3JMAe3k,en3#9np4?XFx4Gato4i63u4Gj,u5fTy% 6ciK|6@D^66cZ8+6cQ5+5;$2*6&?T\5;u!:4GRef2nYuV3k,bk3JD1c3JVJg3JVMj3#9qx7aABD 8XO?V9>cx90ye 855;&@@4?yv\78Mm07Zw!86@4v46@n>87Zwy05;ly? 6ciK*6A#..5D*m:5DpFw4Gaql3kwSi3#0bi3JM7c2-4Ag3kwVl4h\6&|g38X6ZB6@M^56&?T|5;cj_4?ys|7Znr<6ciO06@4s278,>77Znl/5D.a, 6ciK*5fBg@5fl*-6Aldx3#0bk3#0bi3kwSh2+:-a2nq-c2-4Dj4h\6cZ8*6&|c>78e^36crT|5;Tav2nGiT2+z&V2+|4f3kwSi3kwPf2nh#e3#Iwu6crT= 78Vy88W~850ye 85~cK7Z;BVFflACEHEl4Dk>#%BqAUoA66@M@76&|Z|5DyOy5EK!/6&?T/6ciK\6crT<78e^26&?K#5D.X& 5;u.%5fBj:6ciK/5DpCt3kwSj3#0Yh3k,Yi2+:-a2+:|c3JMGj4h\/r3kwVk3#9km4igd?6&|T| 6&|j46@Ds26ciK\6&|c>6@4f=5;$2-4?E+m2L#ZS2nYuU2nY&b3k,bl3knDa2MP?e3#Itr5;$5\ 85kKG9>c_D0^f 85~TE85txoGcYSFFEA=AD#R1|C-3W$Ff1xAFe;f2CL~aZ5;Tj&5DyOy4h<0x6BHB+5DyR^5D.X@ 5;lv,5D*g.6cG*!78e^478e^36&|g16&rE$5D.X~5fl\/6cQ5-6cQ8*6crT|6@4i>6cZ2:5D.U_ 5D.d.5;u$-6cP>:4h\/r3kwSi3kwPh3#9kl3JMDf3JMDf3JVMl4h\+p3JMDg3kwSj4iga,6cZB* 6&|g378Vv36&|g06&rB$6ciK|5;&\!4Gaqk2nGiT2nYuT1/lWU3k,bm3knDa2n_4h3#9np5fv5= 8X6lNA\rGI0@g 85%ZG93n6/H8wObF;u18D#8#+B/=B+FfT4NG@3zNEGHio6cG$.4?O6v4?XF~6cZ8#6B7\#6A#.! 6B7*:6A#@.6BG>?6cra16&|c=5;>B\6cZ8#5fT^!6ciK\6cQ5-6ciN<6ciK*6&|c=6cG!,5D.U_ 4?gR@5;>B\5;cj_4h\45;lv,5fc!+5DpFv3knMh3J3#X2nYuU1/cKR3k,bl3knGd3JVMj3kwVk4!2<0 85H>6cZE*6ciH*6ciE*6ciK/6B85*6ciK|6&?Q*5fBa_5EBm@ 4?gU.6&|Z<5DyLx4h\cn6cQ5%4?XCx4?XI^5;&!:5;u.!5fKs: 5;ly.5fcy;6A#.!6BHB/6ciH*6c|v46cZE*6cQ2+6cQ2#6BHB-5;u!#5;u!%6cQ2%5DyOz5EBs. 5fT@-6&|T+4h\>JL0yf 9UL1VEHgJYH^s*xJv=#~IXW>nH8wLbFflnhIW^aYDkL2h6c7@@4?gLy4h<3^5fKm@5;$2$5D.a; 6A#..5D.U_5D*p%6&|c=6&|T*5.HX15;&*#5;u.$6&r8$6BH8+6B7\#5fKp.6cP>:5D.X~5D.a; 6BHE<6cP>:4i66x4h\6ciK\6&?Q-5DpCs3J3#U1/lNP2-4Ag3J3?T1qTNQ2L&NN3JeSk3#9hk2+z?a3kwSi3#b9\ ARZndBO-m^15p 8yg=rJ3T_?J3T_.H8wUkIXN\rIypBuH^RvqIXW*gEGZ_s5DpFw4?XFy4?XI_5fKm&4?pd;4?O9^ 6B7*:5fBd_4?gO^5fl\*6&|T*6crQ\6B82#5fl\/6cZ8$5fKj~4i66x5D*m?5;ly.5;&*!4?XL, 6&?N+5D.X&5D*m,4h<0v4h!:p4Gs%s4Gato4Gaql3JMAe3kwSi3k,Yi2+|4f3k,eo4i6C&6A=*% 5;>L26cQ2+6&?N*4h^wi2L#ZQ1/lNP2+|1d2+z_S1qKHO1/T5J2n_4h3kwSh2+q_Y3kwSi3#b3$ ARrzhCnYB.1Es 9v><%J3Tx:JUlx&H8+prIX5.rIyo>mI5\*rIW^aZGAkMs5DpFx4?O6w5D.X~6B7*.4iFO;4?O6w 4?gO~6B7$,4?XI_5fT.+6&?N*6crT=6cZ5!5E2v$6&?Q*5fcy@4h\6cG*!6&?Q#3%+JZ1/lQQ2L=cS2+:-a2+z_T1/uWR2L#TM1/=_c3kwSh2+z?a3JMAf3#R6cZ2:4?ym#6&?T/6B82%4?O9x5D*g@4?OC~6B82#5fKp; 5fBd^4?gR,5fBaz4h\+ 6&?N*6A#.:6A&sz3kU|V1/uZT2L=fS2nYxX2+z&V2M7oU2L=cP1PKca3kwSi2+z?a2+:|d3#R+; 9UmelBq1dy1W! BrG^DH^j@sIz2r.IypBuIW\vjHaRvqK0iM-G&hYQG9VNX4?XFy4h\$6A=>%4h\EG8l~AQ3#0Vd1/uWS2nYxX3kwVl 4?yp$6&|Z<0sd ARZkY8yp;O8yp;O92.&K92.&M92plE85bHD6&?N$5D.X~5fKm;5DyR~5fKv?5;cm~4h\K1Ox.F1Ox*K1/lNO0L1qB8L0$6cG$:6B7\:4?XFz5D*g,5;&*%5D.a&5D.X~4?O6v4h^~p4Gj~q3#9np4?XCx4?pg: 5fKp,4?O6u3#9km4Gj~q4Gjzo3kwSg2+:-b3kwSi3#9kl3JMAe2nh?c3k,hu6A&s^4Gjzn3ke7c 3JM4Y1qKHR3kwPc1/uWR1/lKM1qB2I1Ox$H0-0ye 8yXoI8X6oN9334R8XO&P92*|R92gZ978Mc+4i6C~6A#@;5D.X~5DyOy4i6C^5DyLy6A=\$6A&y: 4?O9y5D.a;6A#.:5fT^%5fBg&5fc!#5fKp,4?XI_5DyOz4h!%r4Gato4Gs%s3#9np4h<0x6B82% 5EBy?5DyLw3#0bk4Gj~q3#9kl3kwPe2L=iW3kwSh3kwSi3JM7c2+|4f3#Its6A&y&4Gatm3I/;X 3JC|U1O;>N3JVDa1qKEN1qB2I1Ox!G1Ox!G0%5fc^.4?O9y5;#>#5DyOz4?XI^5DyOz 4h<0x5DyO^6A=$?6A&y@4?gLz4?.p:5DyOz4?XIz4h!%s4h!%q3#9hl4Gj~p3#9kn4Gs%v5DpFw 4?pd,4h!%q3k,en4Gj~q3#9km3kwSh2+:-b3JVGe2nq-d3JD1b2+|1e3k,ep5D.Uz3#0Yh2nGZM 1/lHJ0tE.M2+:?V1qB5J1Ox!G1Oo^C0s\pF1OovA1O;>L1q1>J0E 0t5vG1qKHQ2nYuT1/cEL1qB5M2L#NI1O;\J00!k 9v@PV8XO&R9v@VZ9U2uB5DpFw4i69~5fKp.6cZ2:5D.g:5fT^%6B7\?5fKv?6A#.:5DyLx4h\%4?E/q3#9kn4Gatn4h<0v4?gL_5D.Rw3kwSi 3JMDh3#9km3#9kn4Gj~q3#0bj3kwSh3JD4d3kwPe1/lNO1/lNO1/&ob4Gatm2+qrQ1OoyB0-0^g 9v@YZ9UdVdA0Z,Z6cG$;4h!%s6A&y,5fT^#6ciB%4h\+5fBg& 5fc@?5D.X;6cG$.5DyO_5;&\$5fBd_5D*g,5fKv:4?EF1Ox!F0L1q1\I1/lKL0L2M7rZ 3k,bm4iOX$0\m 9v@GP8y:AR6cQ2:4Gs+v4?O6w4?XI^5fKm,6B7*.6BHE\5f2Xz4i66w4h\M2nY_a 3k,bm4iOa*0\m 9v@VW9UvbS6cP\;4Gs+w6B7*:5D.X&5fKv!5;lv@5fT@%5fBd_4?XI_5DyLx4?XCw4i69z5D.X~ 5D*m.5;$2+5DyLx4?.g@5D.X&5fKp;5;#$;4h<0y5f2Uy4Gj:w5;u!%4?O3s3#Itq4Gj~p4Gj~q 3#9no4Gatm3kwSj3#9kl3JMAe3JVJh3#9km3knGb1/lNO2M7rY3kwSg1/cBJ1O;>K1Ox.E0F0s!a70RaI50s!a70s\mE1qKKS3JMAf 3#9kn4iOa+0\n 9v>ndA0QYK5;lv@4h<0y5;&*#6B7\!5fc.#5;lv@5fT^?5fl@;4?XR.6c7.;5fBg&5D.X&5fcy, 4?XI_5;&\$5;Tdz4i69z5D*g;5fKp;5fcv&4i69_5DpFw4h<3^6ci8:4h!:o3k,eo4Gj~q4Gj~q 4Gj~r4h!%q3kwSi3#9kl3J3|b3kwSi3k,bk3JC=Z2L=fT2nh?c3#0Yf1/cBJ1qKEN1Oo^D0<5d8 0%5;u.?5fBd^ 4?XFz5fTy?6B7$,4?gO~5fTy?5fBg~4?XCv4Gs%t4h!%r4i6C^5f2Rw3#0bi3k,en4Gj~p4Gj~r 5;u^;5f2Uw3#0bj3kwSi3JD1c3kwSi3kwSi3J3-Y2nh?c3JVJi3#0Sb1qB5L2M7oU1qB2H0s!a8 0F0s!d90s!a906ciK|6cQ5%4?gR&5DyO_ 5D.a;5;&*%5fKm@5fKj~4h\+6cZE|7^bQD6ciE+5;$2*6&|c=6B7>#6ciK-5DyOy 4h<3_5;u.?4?O6w5DyLx4?gR~5DpFw4i66x4?O3t4Gj~r5EBy:4?XFx4h!%r4Gs%u4h\/r3#0el 3#Itp4Gjzo3#9hk3JVMj3#9hk3knGc2nPoS2M7oU1/ufY3#9kl2+quS1/cEL1/uZT2nPrW3JD1b 3JM4Z2L=iU2nYxV1/lNN1/lNN1qB2H0t5sD0/6@M.A8yg_K6&?Q*5;u.!5fBdz 4Gj:v5fKm&4h<0x4?XCw4?gO^5DyR,6cQ2?4h\96c7^&4?gO^5D*m?6A&y?6&|c=6c_sC78Mm07^JKJ8yg_J6&|d17^0!36&|Z<6c7@~ 4Gj~s5D.U_4?O6v4h\-78Mi=92*_E6@fEO9vT|M7_+@16A=\#6ciO092*?L78DQ! 4h!%s5D*d~4?O6u4Gs+w5D.a@6BHB|6&?T\6ci8:4h<0w4?XFy4?XCw4h\B\6&|c>6&?Q/6B7>:4h<0w5DyLx4?.p,4Gawp3#9hk3kwSj 3#9km3kwSg2nY&a3JMDg3#9no4Gatm3JC=Z2L=cQ1/ucV3JM7c2+:-Y1qB5K2M7oU1/uZU3JMDg 3k,bm4h\%5fBg~4h<0y5;u*/6&?a38W>VX8yFQ55;Tdz4?*^-78V^B9>>GN7Znr> 6A&y,5DyO_5D*d&5D*m?4?XR;5;$5=7^0$46&?T\6A=!,4Gs%t4i66x5EBy;3#9km3#0bj3kwVk 3#0bj3kwSh2+|4f3kwSi3k,bm4Gj~p3knDZ1/uWR2L=lX3JD1b2+:#X2L#TN1/uZS1/lQQ2nY&b 3k,bm4Gj~p3knJf2+z&T1qKHR2+z&V2nh?Z2L&KL1q1*E0RaL60s^R50t5vI2MP?e3#0Yf2nPlR 1/uZV3#R,v0jX 8X6T86crT|6A#..5D.X^4h<0x5;$8<6crpE9vT-L85<&W7Znl|5D.X.6@M!D8y6QA6&?T\6cQ2# 6A#@.6B7\!6B7\?5fTy!5D.g:5;u$*7^9\66&?T\6A#y~4h\47^%xL6cP>$6@W8OAQu_A6c_d06@4m16&|T/ 6B82#5D*g,5;&$!5DyR@6A=$.4?O9z5;$2+5;u.%5f2X^5D*j:5;u@.4h!:o3knGc2+:-a2+:|c 3JD4e3#Itp3#9km4Gj~p3#Ito3#9no4Gaql3JM7b2M7rX2nPoS1/lQQ1/lNP2nq-d3JC=b3k,Yj 3JMAe3kwVk3knDb2nh?b3JMAd2nPlS2+|1d2+z_T1qB2I1qB8K068yy&F6A#\=9V8 78Vv26ciB#5fKp,5DyR@5;lv@4?pd:5fKs:5fKp@4?O6w4?XI_5D.Uz4Gani2+:#Y2+:-a3JMAd 3JVMj3#Itp4Gjzo3#Itp4Gj~p3#9kn4Gaql3knDa2M7oV2L#WP2L=fS2L=fT2+|1d3JD1c3kwPf 2+:-b3k,bl3knDc2+|4f3kwSh2+z;Z3kwSh2+qxU2L#TM1qB5J0<5d91qBBP2+:#Z2+:#Y2nh#d 3kwPg2-4Dk0jX 7Zel=6cZ8+6B82+5fc^:5D.a,6&|d08W@DU9v@4H79JiR 8yp-U78DZ|5fBd_4i66y5DyOz5D.a@5D.X~4?O6v4h!%s4h\L1/lQQ2+|1d3JMDh 3kwSi3JVMl0jX 6&rB$6cZ5#5;u$+5;cm~4?XF_6&|d38W7ak;c9>l|SAt4zV A0Z,d6&rB-6cY>;4ips-93CDX7_+@14?O3t4Gato4Gato4Gj~p3#0Yg2+z;X2nYxW2nY_Y3JVJi 3#Itp4h!+t4h!%r4Gs%t4Gjzo3#Itp4Gatm3kwSi3JM7b1/lNP2M7oV2nYuV2nq#a2nPoS2L=iV 2+|4g3#9km3#0Vd2L=oY3JMAe3kwSi3kwVk3knDa1/cEM1/c8H0,kA0i/oBNrDS BqSIV5;u$-6&?U6FE&~pM-K/HIxs6F92FD=3#9km3#9km3#9km3knJe2nYxX2nY_X2nY_Z3kwSj 4Gj,s4h<0v4h!%r4Gs%t4Gjzo3#9km3#9hk3kwSi3kwSh2+z;Y2+|1d2+:-b3kwSh2+z&U2L=lX 3kwVl4Gato3#0Vd2nY&a3JMAf3kwPe2nh?b3JC=Y1/lKO2L#TM1O;>M2nY_X1/lQR2nh?b3J3-Z 2+|4g3#9nq0mX 5fBg&5;&*+6cQ5#5fc@.4h<6@6&?X27Z,>D8XF?YAtNIqEImCf9>Bc36BQU9BO-RmAOAs\9uARr+d79cGx 8x^*07aJQFDL/b2SzBCOUS3*WR_@JoJvufoBpekK4Gaqm3#9km3kwPf2+qxV2+:#Y2+:|b2+|4f 3#9np4h\At4tpM*IH.TU=jVUtnKcT2-j-L/j%|G&6~/92E|+4Gjzo3knJe2+:#Z2nh;Z3JMAe3kwSj 3#9np4h\/r3#9no4h\/s4Gatm3#0bi3JVJh3kwSj3#9kl3kwSi3#0bi2nPrX3JVJh3k,en3#0Vf 2+:|d3#9kl3#0bi3kwSi3knGc2nPlR1/uZU3JM7a1/cKS3JM7b1/cHP2+:-a3JVJg2+:|c3knJg 3k,Yi3JVMl0jU 4iFO.6&|c=5;u^;5D*g,5fT^!6c?g06@Dy9A0i+f8zCVX85tH76ciX5A1Nsy9UB;HBO+L8AR?%c9U@VT85b236Br.OEiE7x6&|dFB*MSXD%2dR 5.8*b9VA0fR8@=8US42fVPRlhTv=95N#QdUKYVB/~OV85I?>6c|>eFd?Ec6ci.S8x 6,S?XBSTJ6SX.0MU0z$cUteBcUR/.USy4/,Nk@9OL/j+<7^ScZ9TgN67_,v9At4%X6&_E-BqtRV7Z,.36&rB- A0izcI7m&TQdL~>R~5zJU0hpRT3TCMT2fC?OH52lNk@0DJUu?#LqkGAKRq-oAs7w<3JMAe3JMAf 3kwVk3#0bj3JC=b3#Ito3kwSi3knJf3kwSi3knMg3J3#X2-4Di3#9kl2+:|d3#9no3#Itp4Gjzp 4Gj~q4Gs%v6A#^&4Gj~q4Gj~p3#0Yh3JVJg2+:-a2+qxU2M7oV3JVMj3knJf3JD7e3JMDi4Gaqk 3JVJi3kwSj0dR 5E2m;5fBg~4iFO&4?XFz4?XCw4i66x5fv5=8XO;T6&_H<7ZevE85b876cQ90B*VSG7Zw^26&?N| As|,gJ4Q?PPftxvQ,9YGTU&RNT3T9KT31p=QBqP;N#QRLK0iP>LPSJELO,n#EF&\Z3#0Vd2M7rX 2+|4g3#0bi2+:-a3k,em3#9km3kwPg3kwSi3kwPf2nY_Z3k,bl3#9kl3JMDg3#Iwq4Gj,s4Gaql 3#Itq4h\E>8yy=M6cQ8<78M*K6&|f>5;u<6926847Zel>6&?N| As|,lJVZ_IOiN2nRa^tIT3T9KT3T9KT3A~3QBqP?Oi4vSKtMo1KtV_6LP0,|FC|&v6buLk1/lQQ 2MG_c3#9hj2+:|c3k,bl3#9kl3knJf3JVJh3knDa2+|4g3#9nn3#9nn3kwSi3#Iwq4Gj~q3#0bj 4Gs%u4h\/s4h!%s4?XFx4Gjzo3kwSj3#9kl3kwSi3J3#X3JVGe2MG_c3#0bi2+z?a3k,en4Gaqk 2+z;Z3JMDi0gT 4?gLz5EBv.4?O6v4i66y5D.X~5D.U_5;$5|8X6Z85fT.|7aAWJ6&|g05;u\>77|K/6&?T<6&?N\ AsHDeJx4=HNJ$ggQdL~\Q@Un$Q,9VDT31,6QBhG.O?e$YLP0#2K0ZG5;u**6&rB*6&?Q\6&?N= AQcrVJVZo9L|6nNNlZ.jOH4>kOjA=+S65e5Qc:P:PfSTiMngkEKtMo2LO+t+G&Y729u.W63%+Pd 2nYxY3JMAe3JMDg3kwSj3kwPf2+/=d3k,bk3knJf3kwVk3#9no4i63v4Ganj3kwSi3kwVl4Gawp 4Gj~r4h\6ciN>7aJB76crT<5;&>/6cQB|6&i2$6&rB/ 9>BlXJVHT1K<,-7K=1GKNJmCSM-dgmR8@|2R^H:-P;~lnM-B/MLP9=7Kt4P:GAt#5AsHAL78Vr* 3keAd2+:-a3JMDg3kwSi3kwSi3kwPg3JVJh3kwPf3JVMj3#9no4Gj~q3#0Yi3kwVj3kwVl4h\B|6BHE\6&?m86&Y*?6&?N\ 8Wk2SJ3c?$KtVx4K<,|BL/~JBLPSVQO!J#?Q@3P,O?e>gMnywILqR=5K0P|,HZU~NDI.\q8X/bl 6&7ju3JC=Z2+:-b3JVJh3kwSi3knJf3JMDg3kwPg3JVMj3#9kn4Gj~q3#9km3#9km3#9np4?XCv 4Gaql3k,en4Gj~r4i66x4?O3s3#0bj3kwSi3kwVj3knJg3knGd2nPlR2L=fT2+:=e3#9km3#0bj 3kwSi3knJh0gT 4Gj~r4?gO_4?gR@6B7\?5D.a@5DyLx4i69^6A=!.5fT./7Z,&>5;>B/6crT=6&|vB77|K*6&|T\ 7_+$RH^j!yKR_M/KS4r5K2=8Wt215;$2-6crT=6&|j36&rB-6&|T\ 7_+!PHa0dqK0iG$J3T#|K0ZD-Jv>B3NlHyiN#ZpcN#i^hNk@9NLPA19K#&?Iy5RL EiEe>7ZeQ@2nPlR1qcfZ3J3-Z3JMAd3JVJh3kwSi3JVJh3#9no4Gj~p3#Iwr4Gj~p3kwVk3#0bj 3kwSi3#9nn3#9km3#Ito3#9hj2nPrX3k+nr4h^~n3kwSh3JMDf2+quS1qB8O3kwPf2L=lW2+z?a 3JMAe3JM4c0aQ 4Gs%u5EK.$6A&y.6BHB/6Aup&6B82$5;lv@4?XI&5;u$*7_+u<6BHB*6&|Z<78e^36cQ2+6&|T\ 7ZemDE?*ATJUllvG@njqI6OW;JU@4IH H7qPGG@Cg\6&q*_3JMDf3J3|a3JD1b2+|1e3kwPg3JVJh3#9no3#9hj2+|1d3kwVk3#Itq4Gaql 3kwSi3k,eo4h!%r4Gjzo3kwPf2nPlR2MG_b3kwSi3kwPh3knGZ0$5fTv:6ciE- 6&?X3A0j0xD#R1\C-3T,D#!&OG@wvxK<,#6K<-4EL/J1Oo@F1qTQT2nY_Y2+z_U 2nh?b3JC|Y0XN 4Gj,t5D*d~4?gX.6B7\!5D*p%6cZ5!5D.X,6A=*!5DyR~5;&.;5E2v$6&?Q-5fKm@5D*g;6ciK\ 6cQ2*7^~&ZBqAUmA0HqhCMPQ\FEThdK0_Y,3keDe3JMAe3JMAe2+:-b3k,bl3#9km3#I:^6@4yCA0Qwf78Dc/5DyLx 4?O6u3#9hk3#9no4Gj~p3knGd2+z;Z3k,bk3kwSi3kwVl4Gjwm2+hlP1Ox!H1qKHP2L=iU2nGfR 3JVJh3knDc0XO 4Gj,t5D.U_4?gU,6A=$:5EBy%6B7$;5EK.+5;ly;4?XCx5fBg~5fT@+6&?K#4?XFy6B7$;5fT@+ 6A=*$79AfSA08PP7^toTA<;y?EG\&RJU&#!K0?o5KK1qB5K1/uZS1/cEN 3JVJh3JC|Y0XO 4Gs%u5fc.?4?XI_5fKp:6B82+6B7$;5fl*+5;l@%6B82!4?XFy5E2p:6cP\.4?O9y6ciH-5D*j! 6c7@.6@M*H9v@PS8X6lL9U@~rC-eBEI6OQ@JUu#$K0iT2MMFeILPA7EMMXwNMn.~HKRrD+K<;AP P.G7,PDMQ\7_,u>5;Tav3J3-a3JVJh3k+,~6crT|4Gs:r3#9qs6@4!I9Ud7O9UU7N6&|j6A0HYP 6&_B%5fT@$6Aldv3kwSi3JMAf3#9km3#Iwq3%+Se2nPoW3JVJg2n7WN1Ox$I1qKEN1/uWS2L#WQ 2nq-d3knDa0UM 4Gj,t5fc.!4?XCw4i69&6cQ2$6B7$,4?yg.5fKs:6A=*?4?XFz5D.a@5DyLy4h!+w6ciH+4?XL, 6A&v@5;$F69vvGR85~ZI8z3YmC-wKFGdDUsJUl_!K0ZD|KJ1qTQT2nYxX2+:#Y 2nh?c4Gang0UM 4Gj,s5D*m.5DyLx4h<3z5;l^?5;cjz4G^56&|d29UvwqC-nEFG@VRmIXOE@KR?S/JwHA-Jw7=!J3v4\KtVn-HZUzMFgHR! Oi;r;RZ$t&KPeg@6&|c>6&|c>4i6I:BPb\~As|+jB|PW%85I#>5;u**6&|pDAt4,U6cZB+5;$2/ 7abfL7_+@26b=sy3ke4X2M7rX3kwVk4Gj~q3#0bi2+:#Y2nh#c3JC=Y1/lQQ1/&fW3JM7c3JMAe 3JVJi4Ganf0RK 4Gs%t4?XIz4?XCw4i66x5D.X&5f2Rv3#Izu5D*g.6A#@;5EBy%5;cj_4?.p!6ci8!5;u!?4h<3^ 5;ls&5D*m:6&?T\6ciK\9UvqoC-nHFGcz%ZG@edtJv==+K0ZG-J3BczI6FEzJv=?_GAJV$Bq#pP M-vvrR8@w;MLQ=O78x8I8XhDhBq0+P7AGw@BOf0f9UmkpCM6!m6&?T<6&|j386qMd6&?T\6A#!! 5;$5|7^bNF6&?H:4GRee1/lQR3JVJi4iXX~3#9km3kwPf3JD4d3JMAd2nh?b2+|1e3kwPg2+|1d 3JMDh3#0Sa0OJ 4Gj~r4h\Nt9UUb~ Jw_#PQBhD~NJBRw8WtEI9>>VaCnqEq7^=DtD%mN_BNrDNBrGf+9>u#D6crX17Z-TUA\rGG6ciK/ 5;u!$5;>B\6&?K%4h^wh1/lNO2nh?c4G^_;BO;9X6,A;fBO-Uh7Znv17Z+>AA0QqW6&|Z= 6&?Q-5fTy?5;ly;4?E+m2L=cQ1/uZU3k,en4Gj~p3#0Yh3knGc2+:=e3kwPh3kwSi3kwVk3%+Mb 2nh?b3JC=Y0XN 4Gj~q4h\ 6@4i>5DyOz4?.p:4?O0p3JC=X1/uZU3k,eo4h!%q3#0bi3knJd1/lTU3JMAe3kwSi3kwVk3#0Yf 2+|1d2+qxU0UM 4h!%r4h!%s4?gLz4i69_5fKp,4h!%r4Gs%u4?XFy5D.Uz4?pa.6A=$!6B82-5;u!#5;ls~4iFO; 5fKp:6A=$:5D*p-A<;p~CLtgpBPlB*E?x#JG@MFhH^j@tIypHwI5!!mH90vvK0Z7#H_mn37_+^0 6cryOF.|gxKtMe-Gc6%+8X6uR9vm1I6&|pHCnzW;8yg;V7^0*99UB|O7^J597Z,$78X6oO92ON6 6crT=6cY>.5EBy!5;KRt3JC=X1/uZU3k,en4h\/s4Gatm3kwPe1/lQR2+:|c3k,bl3#9km3#0Yg 3JMAe2nPoS0OJ 4Gj~r4h\/s4Gs+v4?gO^5D.X&4?O3t4Gs%t4?O9x5D.X~6B82+6A=$!5;u.#6A=\$6A#@@4i6C~ 6B7\!5fTv?6@-lfEiEf6CnFypASWy#Dk$>4FEBJTG@D0aGBP~cH^jvkG@MLnJU$4/J>&foA\G#A 5;$5#5;$2-5;u!#5;u.%5;ly:6B7\?4?XI^ 6A=$:5fvXOEif#IHa0UcDkLBt9waL@CnqW/Dl0EBGBGePFfcGMGc:/ZGA=PQH^%B@KR?VB\6&rB#5f2Ux3#9hi1/lQT3k,en4Gj~q3#Iqn3#0bi2nPoT2L#cU3k,en4h\/r3knJe 2nYuW3JVGf0XN 4?O9x5D.Uz4Gj~q4i66x4?XI_5DpFw4h\$5;lv;5;&!?5fBg@ 5fT@\Br7yGH_+CaIXpZzFDoG;9w8~nB/%>.B/&5+EiN&JE?WlCF.7qREGH_!D#^&XKtn|CK:CnqK;A#6B7\?5E2s:5fKp: 6@fizIyW$jGBPwhK<-19H^90A9UUJcB/%>.B/~$,DJ,B8DJd.5GcheIC+XvlAR\X|JV8cBM+.R, 8Wa_-4?pg%7^<|dB/<>&BO-Xp85%&Z8W~KC6&i5+7aAED7^0$78X6iK7_+^16&?N-78n>JARikU 8W%H56A=\#5fKm@4h\/s4Gjzn2+q_Y3#Iwq4Gj~q4Gjzo3#9kl3kwSi3ke7Z2-4Dj4Gjzp4Gaqj 3JVJi3#0Yh0XO 5fBg;6A&v.6Aumz4Gj:v6B82+6A&s^5D*m?6B82+5;lv,6B7>#5fKp.6A=$!5;&*$6A#.:5D*t1 DlIoWJU28kF.7:sNJvOWJvA$R9vvMaBqb^_B/~!^CnzZ*DJd!8GcqqLCLkLd8y/VyIX..2M+*q7 FB=vQ4?XI^6&|^IAtfXuB/=5&ARrze9T.oE7_,l/6@M!C85tH97^bQJ8yOW86&|c\5;$5>9UUDU 8yOW75;lv@4?XI_5EBv,4h!:o3J3#X3k+nq4h\l|QA*6@f2G 92yoC6cG!@4i69z5fT^%4?E%6A=*$6@M>K9wH/sB/bOb7^~oK7Z,.25;u\<85~ZI7^0\C8XX?M6&?a06cG*?6ciO2 92.;J6cP*;4h<0x5fT^?4?O3s3J3-Z3#Iwr4i63v4h\%5DyR^5D.X&5fBd^5fc@!6B7>|Cp0&V I5I6LBqARlAu@TnMn.:JJUB8gDk2!f78-NN9v>qjA K$5;>R893LViB/kdi7^JEG78Ml>5;&\|8yp?Q8x7^I|>5E2y/ 92*|O6&r5:4i66y5D*g,5DyLv3knJe3k+nq4?XFy4h\/s4Gj~q4Gatm3kwVj3kwPf2+:-a2+:|c 3knMi3#0Ve0LI 4?XFx4i66x4?O6u4h\%5fT:CF.7nQ B/kswAsZPPC/P0:MnpqFKG9,oe5fT@$5;$5<8y:DeB/tyu8W%TH7Znv16A#$+8XX?R8W%QD92*#L85b257^J886A=>/ 7^tcH6&r5.4iFO;6B7\!5fBdz3#0Yh3k+qv6A&y,4h\/s4Gjzp4Gaql3kwVj3kwPf3JMDf3J3=d 3kwSi3kwPf0UM 4i66y5fBg~4h\+6cP>?5DyR_4?XI^6A=$?6A=$:5E2%FD%>x: 8yg?VAsiVYJ4HrDMM6PAMMXh3GchS278Mm592.=aARr%qC-Co*EiyPfJwHQ3LpUWB6crsLC-+uZ K0_e>Ixry$5D*m!6ciK|6@xQSBP1ds9vB;L7_+@26cG*#7^bTH7^bQG7^#5fKj_4Gaql3k+ns4?XFz4?O3t4Gj~q4Gatm3#9km3#9kl3kwSi3knMi 3#9kl3kwSi0XN 4h<0x5D.U_4?XFz5fc.#6BH5#5fBd_4?gU;6B7>+6A=\$5fKp;5D.X&5fKp;6A=$?5fU3DBqAUl 85tcRAs\F+LqkJAKtMr7OG|.VIWjCK92ON76@W5PAs|L7AR_+lA\Q4L7_+@26cG!:7Z+\66@M!C7^klR8X6iE6@M*F8Wj#= 6crX06&r2.5E2v#5;u.%6A#^&4Gaql3#Itq4?XFy4?XFy4h\/s4Gato4Gatm3#9nn3#9km3kwSj 3#Iqn3kwSi0XN 4h\7PEJllJ2EaSBOMtP6@D.HAR_2Q05;u.#6ciK\85%oRAQ#=J7_,o|5;cp;6&|g06@D^A8X6oP92*-L6&|j88y6K6 6A=>*6&?E?4?pd.5;lv,5D.Rx4Gaql3#Itq4?XFy4?gU;6Aumz4Gj~q4Gatm3k,bl3#9km3kwSi 3#9km3kwSi0XN 5EK.?4i63v4?XI^5;$2+5;&$?4?XFy5D.a;6B7>%5;&*!4?XFy4?pX@5D.a;6A#.:5fm9CAsZPS 8yXuOD+dX.Ks|G.H~Fs8PEJWfJvcKiCm%Xb6&|mA9v$ke85tWJ9UvklD#s_YLq%5DyO_5fT^%6B7>%5;&*!4?O6w6B7>%5D.X&5;u@:5fU3C9vK?P 9vmGhJwHA-H_@/QH9tpAPD,$PIyyHoDI.$l78Mm393CGa9vm7P7Z,\FAtx+0JwirDL*$?M6A>30 9U$zoB/~\+FDfAw5;&\-5;lv:5;$2/78Mm06&r2;4?.p?78Vs06crT>85~cK9334S7Znr>85>,/KRY;xE?NW2H9<*DNJK~8IXO2nC+:Bw8y6K785%rTARZYQ7Zw@486hSrGCDy%ML!,t78Dc= 6@o8LAR!F&FD-~>6&rE*5fKp:5fKs?6ciK|6cG!;6ciE+6@4i>6crT=6&|j69UUDU7^I\378;EH 6&|W/5;&\#5D.g:6B7*:4?O6u3#9km4Gj,t4?XI^5D*j:5fBd^4?XI_5DyLw4Gatn4Gj~q4Gawo 4Gj~q4Gj~p0aP 4i66w4h!+u5D.X&5;&*$6A=*#6A#@,5D*m?6A#!!5;&*#5f2X_5fTy?6B82#5D.X~5;$XI9>l|O 9U,S9J>#TeB/t^~Iz@fDL/j/56@WEUD$6JmMMOR=BNh=C 6c_pA8z3exE?fx88Wa;<5;ly.5fc.!5;&$$5;u@?6ciE*6@4i>6B88/6ciN>8XFrK7^9*36@4m4 7^I>25fKs;4?gR@5;&$!5DyIv4Gawp4Gj,t4?XFx4?XI^5fBd_4h\#6B7\?5;>dE9T*xH 9xpmPIW^OIAt53xJwif5LO+z46crmFBP=!QLPJ78G9w&m 6BHH>8XY7pEiEl9A\rGE5fBj,6ciE-5D.X~5D*j.5;u.!7Z,@05fTy?5;u$-7Z+>A7^9\56crT> 8X6T85fBg&5D.X&5fKs?6Aumz4Gj~q4Gs+v4?gIx4?XFz5DyOz4h\/s4h\%4?O6v4i6C&6A#.?6&|T$4i66x4?pd:6crT>7Z,$56ciK\ 7Z,.25;cm&5D*g@4?XL&6A&v~4Gj~q4h<0x4?XFy5DyOy4?XFy4h!:p4Gs%t4h\/r3#9no4h!%r 4Gj~r4h!%q0aP 5D.Uz4h\$DN7Znr>6@f8K9UK=O8W%K879AokGB?FhFe-Mz 6cG*#6crgAARr,kBq&5z6&_B%5fKm@4?O6z6A#$#5;u$+6&?K!4h<0w5D.a@5;&$%6@4p26&rB- 6&|Z<5;cj^5D*g,5D.X&6A#^~4Gj,s4h<0x5DyOz5DyLw4h<0v4GRnm4Gj,s4h!%r3#Itp4h!%r 4Gj~q4h!:o0XO 4?XCw4h\$6c_yF8W<&n GBGPAAR8MP7~7_0J2.HyK=SbKKQ=WhBqSsxAR8GN7Znv16c_mA92*|P8X6fD6&|@NDJ+E8D%vNr 6&?Q*6BHF192*=UAtxv~9>BZ34?gO^4?XF_6ciK/5;l@#6cP\;4h\#5fKp;5fKs:6A=*$6c_vE8W<#p Eh_|:8yFWC8_9+EH8nRqLPthFIx^IJA%5fKp,5D.X&6A=*%6c_vE85tlZ BOn/a85kHBAT2U3E?yMjLqk9>F;b?1ARitZ85~ZI8Wj>46&?T<7^bZN92*|N78Vv37Z-HO9>>SV 8W%H45fKs?6crd59U$:rBOewN5fBj,5DyLy5;&$%5fBd~5fKj~4h\$6B7\?4?XFz5fc@!6crd47^trV 9U2?M8X6lMCM:r$Bq#@TKWJ2fpRBq1Ol9vvGT8W6@M!A 7Z,*978DT$5;u$+7aJZP9vv7J5;cs;5fKv!5;ly;5D*j:5fT^%6A=\$5DyOy4h\608X6iK 8yXuN92y_UDl8=-DK6&?T|5;>E< 6&|j37_,o/5;u.!6&|m57^tcE6cQ5+5;&\-5fBg&5D*j.6A=*%5;u.!5fBd^4?XCw4h\/8W6@Dv35;u.!5;&$#5DpIz5D*j:6ciB#5fBj,5DyO_6B82%4?XFz5DyOz 5EBy%5;cp,5;$2$5DyOz4?XFx4h!%r4G~3~6Aup^4h!%s4h!%r4Gjzo3#9nn3knMi3#Itq4Gj~r 4h\#6A=*#5;l@#7Z+\8 8X6iL8yzAjCnO>\JUcThCnFyl8yFQ86ciK*5;u@?5fBg@5D.U_4?.p:5fTy?5;lv@5D.X,6A=$: 5fT@$6crQ\5Dya.6BHB-5;#\#5;ly.5fKp,4?O6w5EBy!6cZ8#5DyO_5fBg;6B7>%4?O9x4?XFy 5EBy%6ciB#6BHB*5DyOz5fBd_4?O3t4iFO,5;lv,4h!:q4Gj~p3#9kl3k,bl3keDf3#9kn4Gj,t 4?O3u4h\!5fKs:5;u@:5;&*!4?XFy5D.d.6A=$!6B82$5fBj;6c_g3 8X6oN8y/bpBqcXIJTNFFAs_eO78Mc/5;u.:4?XFy4?XI@6cZB#5fc.#6B7*:5D.Uz4i6C&5;lv, 5D*j.5;u.%5fBp?5;u.!5;u.?5DyO_5D.X^4?O9x5fc.%6B7>%5fc.#5;l@%6B7>%4?O6v4?gO^ 5fTy%6ciB#5;u!%5fBd~5DyLx4h\/s4?gX.5fBd^4h!:q4Gj~p3#9hk3kwVj3JMAf3k,en4h\2\ 8X6oO9UvwnBQHHZG~~b?9UB;E6ciE+5;cm&6B7*.4h<3_5;u!%5;u!#6B7>%6B82+4?XI_5DyR^ 5fKp.5fKp,5D.a@5fKp,4?XFy4?XFy5D*g.6A&v@5fTy?5;&*%5fKs?5;u!%5;&$#6Aum_6B7\! 5D*j.5;&*$5;ly;4?gLz4h\tjBQ?fNCm%Ue92ylA6B7\?5D.X&6B7\:4h<0x5fKp;5fKs:5;u.?5;&*%5DyOz4?XFz 5fKp.6B7*.5D.a@5fKj~4h!+t4h\$5;l@%6B7>$ 6A#.:5;&*#6B7*.4?XIz4i63v4Gj~q4h<6^5fBd,5;KUw4Gjzn3#9km3kwSi3kwPg3JVPl4h\keEHpAGAsrhU8W~1>5fc^.5DyR@6A#@@4h\#6B7*?6A=*#5;cm~5fc.%5;u.?5fKp.5;&*$5;lv. 5.8X69UUDeFfS%>9UK-K7Zef-4?gR~5DyO_5fBaz4h\$5;cm&5fTy!6B7\!5fKp;5fTy:5DyO_ 5;>R68yp|gDkC5p8ygoD6ciE-6A&y,6B82!4?XFx4h\/s4i6C~5;u^,4h\%5fKm&5D.X^4h\E=78xHVCLtah7^J256A#.?6B7*:5fc@%6A&v&4?O6v4iFI&5;u@.4?O6v4?gO~5fKp;5;&!? 5;u!#6B82%4?O6x5D*g@4?E/p3k,bl4Gj~q4Gj,s4Gj~r4h\$5fTy?6ciE+5fBd_4h\#5fKm@5D.X^4?XCx 5fT@+6@4^GARZhS6&|Z\5fBg@5;&*!5fTy!6A#@;4?XCx5EBy%5fKp;6B7$.6B82#5DyO_5fKp, 5D*g;5;u!?4?XFy4?XFy4h!:o3k,bm4Gj~p3#Itp4Gj~p3#Itq4h\# 5fBj.5;u!#6B7>%5fKp;5DyOy4h!%r4Gato4Gj,r4Gato4Gj~p3kwVk3#0bg3JMDh3kwSi3#9no 4i69y4h\/r0XN 4Gj~q4Gs%t4h\%4?O6w6B7*. 4i66y5;u!#4?XFy5D.X^4h!%r3#9np4h\/s4Gj~q4Gj,r3#Itq4Gj~q4Gs%v5fKp;5fKm@5fKp, 5D.g:5fKs:6B7\?5D*j:6A=*#6B7!&3#9km4Gj~r4Gaql3#9kl3k,bm4Gjzn3JMDh3k,Yk3#Itp 4Gj~r4h!%p0XM 4Gj~r5;&$.4h\%5fKs?6A&v@5D.U_4?gO~6B7\%5;u@?6A=!;4?XFz5fKm& 4?O9^6B7>$5DyO_5D.a&4?O3t4Gj~r4h\$5fBg@5f2X_4?gO@6B7>%5fKp;5;ly,4?XFz5fKp, 5DyR@6A=*!5DyO_4?gO_4?E%5fKm@5;u@:4?XI_5fTy!6Aup;6Aum_4h\7^9\45f2a&6B7>#6B7*.4?pX@5fT^%6B82+5;ly:5;lv,5D.X~5D.a@6B82+5DpIz 5fc.$5;ly;4?O6w4h<0w4h!%r4h\-78Ml<5Dya.5D.a;6BH8$5;&$?4?XI~6A#.:6A#@,5EBy?5fKp,5D*m?5fBg&5fKp; 5fc.#6B82+4?O6v4iXa:6B7*:6A#^&4h\/s4Gs%t4h\%5DyLw4Gs/y6B7*:5E2p!6c7@@5D*d~ 4?gO_4?XL@5;lv@5D.a&5D.X~4?O6v4h!%r4Gj~p3#Itp4h\%5fTy?6BG>:5D*g@ 4?XCw4i69~5fBg&5D.Uz4h<0v4Gj~q4Gj~q4Gatn3#Itp4Gj~q4Gj~q4Gj~q4Gj~q4Gj~q4Gs%u 6B7\$6cZB+5;lv;6B7>%4?O6v4h\/r3#Itp4Gjzo3kwSi3knDa1/lQT3kwSh2+:-a2LuHM2L#WP 2nq|e2+z&W0UL 4Gs+u4h<0x5fTy%6A#.:5;&$%5f2Uy4?pa;5;cm&6A#@,4h!%r4Gs%u4?O6v4h\$6A=$:4?XFy4?O3s3#Iws4?O3s4Gatn3#0bj3kwSj3#9hk3JMDh3#9hk3kwVj 3#9tw5f2Rw4Gatn4Gj~r4h\/s3#9km3#9qs6Aum_4?XCw4Gj~q4Gjzo3#9kl3k,bl3#0bj3#9km 3#Iwr4GRkk3kwSi3k,bl3#9km3#9hl3#9km3#9hk3k,en3#9kl3knJg3k,bm4Gj~q4Gs%t4Gaql 3k,bl4Gs+v4?gO^5D.Uz4h\%5fBd_4Gato4Gs%t4h!:n3kwSj3#0Yg2nYxW2+z&T1/&iX2+:-a2+:-b3JMAe3JMDg 3kwVl4h!%q3knJg3k,bl3#Iqn3#0bj3k,eo4h\M2L=fS2L=fS2L=fT2+|1e 3kwSj3#9hk3JM4Z2M7ua3kwSh3JD4e3knDd3k,hp4Gaqm3#9km3#0bk3#0Vd1/&iW2nPiQ2MG_a 2+:|b2+:=e3kwPg3kwVk3#9kn4Gjzn3kwVj3kwSi3kwSh3JVJh3#9kl2nY_a3k,Yk3k,Yj3kwSi 3kwSi3#0ek3k,eo4?O3s3#0ek3knJg3#9hk2+z;Y2+:?V1p-.C0s!a70s!a91qB2H0RR9100001 0RaI50= #include #include "gb_flip.h" /* all users of {\sc GB\_\,FLIP} should do this */ @#@; int main() {@+long j; gb_init_rand(-314159L); if (gb_next_rand()!=119318998) { fprintf(stderr,"Failure on the first try!\n"); return -1; } for (j=1; j<=133; j++) gb_next_rand(); if (gb_unif_rand(0x55555555L)!=748103812) { fprintf(stderr,"Failure on the second try!\n"); return -2; } fprintf(stderr,"OK, the gb_flip routines seem to work!\n"); return 0; } @ The \CEE/ code for {\sc GB\_\,FLIP} doesn't have a main routine; it's just a bunch of subroutines to be incorporated into programs at a higher level via the system loading routine. Here is the general outline of \.{gb\_flip.c}: @p @@; @@; @ @* The subtractive method. If $m$ is any even number and if the numbers $a_0$, $a_1$, \dots,~$a_{54}$ are not all even, then the numbers generated by the recurrence $$ a_n=(a_{n-55}-a_{n-24})\bmod m $$ have a period length of at least $2^{55}-1$, because the residues $a_n\bmod2$ have a period of this length. Furthermore, the numbers 24 and~55 in this recurrence are sufficiently large that deficiencies in randomness due to the simplicity of the recurrence are negligible in most applications. Here we take $m=2^{31}$ so that we get the full set of nonnegative numbers on a 32-bit computer. The recurrence is computed by maintaining an array of 55 values, $A[1]\ldots A[55]$. We also set |A[0]=-1| to act as a sentinel. @= static long A[56] = {-1}; /* pseudo-random values */ @ Every external variable should be declared twice in this \.{CWEB} file: once for {\sc GB\_\,FLIP} itself (the ``real'' declaration for storage allocation purposes), and once in \.{gb\_flip.h} (for cross-references by {\sc GB\_\,FLIP} users). The pointer variable |gb_fptr| should not be mentioned explicitly by user routines. It is made public only for efficiency, so that the |gb_next_rand| macro can access the private |A| table. @= long *gb_fptr=A; /* the next |A| value to be exported */ @ The numbers generated by |gb_next_rand()| seem to be satisfactory for most purposes, but they do fail a stringent test called the ``birthday spacings test,'' devised by George Marsaglia. [See, for example, {\sl Statistics and Probability Letters\/ \bf9} (1990), 35--39.] One way to get numbers that pass the birthday test is to discard half of the values, for example by changing `|gb_flip_cycle()|' to `|(gb_flip_cycle(),gb_flip_cycle())|' in the definition of |gb_next_rand()|. Users who wish to make such a change should define their own substitute macro. Incidentally, we hope that optimizing compilers are smart enough to do the right thing with |gb_next_rand|. @d gb_next_rand() (*gb_fptr>=0? *gb_fptr--: gb_flip_cycle()) @(gb_flip.h@>= #define gb_next_rand() @t\quad@>(*gb_fptr>=0?*gb_fptr--:gb_flip_cycle()) extern long *gb_fptr; /* the next |A| value to be used */ extern long gb_flip_cycle(); /* compute 55 more pseudo-random numbers */ @ The user is not supposed to call |gb_flip_cycle| directly either. It is a routine invoked by the macro |gb_next_rand()| when |gb_fptr| points to the negative value in |A[0]|. The purpose of |gb_flip_cycle| is to do 55 more steps of the basic recurrence, at high speed, and to reset |gb_fptr|. The nonnegative remainder of $(x-y)$ divided by $2^{31}$ is computed here by doing a bitwise-and with the constant |0x7fffffff|. This technique doesn't work on computers that do not perform two's complement arithmetic. An alternative for such machines is to add the value $2^{30}$ twice to $(x-y)$, when $(x-y)$ turns out to be negative. Careful calculations are essential because the GraphBase results must be identical on all computer systems. @^system dependencies@> The sequence of random numbers returned by successive calls of |gb_next_rand()| isn't really $a_n$, $a_{n+1}$, \dots, as defined by the basic recurrence above. Blocks of 55 consecutive values are essentially being ``flipped'' or ``reflected''---output in reverse order---because |gb_next_rand()| makes the value of |gb_fptr| decrease instead of increase. But such flips don't make the results any less random. @d mod_diff(x,y) (((x)-(y))&0x7fffffff) /* difference modulo $2^{31}$ */ @= long gb_flip_cycle() {@+register long *ii, *jj; for (ii=&A[1],jj=&A[32];jj<=&A[55];ii++,jj++) *ii=mod_diff(*ii,*jj); for (jj=&A[1];ii<=&A[55];ii++,jj++) *ii=mod_diff(*ii,*jj); gb_fptr=&A[54]; return A[55]; } @* Initialization. To get everything going, we use a scheme like that recommended in {\sl Seminumerical Algorithms}, but revised so that the least significant bits of the starting values depend on the entire seed, not just on the seed's least significant bits. Notice that we jump around in the array by increments of 21, a number that is relatively prime to~55. Repeated skipping by steps of 21~mod~55 keeps the values we're computing spread out as far from each other as possible in the array, since 21, 34, and 55 are consecutive Fibonacci numbers (see the discussion of Fibonacci hashing in Section 6.4 of {\sl Sorting and Searching\/}). Our initialization mechanism would be rather poor if we didn't do something like that to disperse the values (see {\sl Seminumerical Algorithms}, exercise 3.2.2--2). @= void gb_init_rand(seed) long seed; {@+register long i; register long prev=seed, next=1; seed=prev=mod_diff(prev,0); /* strip off the sign */ A[55]=prev; for (i=21; i; i=(i+21)%55) { A[i]=next; @; prev=A[i]; } @; } @ Incidentally, if \.{test\_flip} fails, the person debugging these routines will want to know some of the intermediate numbers computed during initialization. The first nontrivial values calculated by |gb_init_rand| are |A[42]=2147326568|, |A[8]=1073977445|, and |A[29]=536517481|. Once you get those right, the rest should be easy. An early version of this routine simply said `|seed>>1|' instead of making |seed| shift cyclically. This method had an interesting flaw: When the original |seed| was a number of the form $4s+1$, the first 54 elements $A[1]$, \dots,~$A[54]$ were set to exactly the same values as when |seed| was $4s+2$. Therefore one out of every four seed values was effectively being wasted. @= next=mod_diff(prev,next); if (seed&1) seed=0x40000000+(seed>>1); else seed>>=1; /* cyclic shift right 1 */ next=mod_diff(next,seed); @ After the first 55 values have been computed as a function of |seed|, they aren't random enough for us to start using them right away. For example, we have set |A[21]=1| in order to ensure that at least one starting value is an odd number. But once the sequence $a_n$ gets going far enough from its roots, the initial transients become imperceptible. Therefore we call |gb_flip_cycle| five times, effectively skipping past the first 275 elements of the sequence; this has the desired effect. It also initializes |gb_fptr|. Note: It is possible to express the least significant bit of the generated numbers as a linear combination mod~2 of the 31 bits of |seed| and of the constant~1. For example, the first generated number turns out to be odd if and only if $$s_{24}+s_{23}+s_{22}+s_{21}+s_{19}+s_{18}+s_{15}+s_{14}+s_{13}+s_{11}+ s_{10}+s_{8}+s_{7}+s_{6}+s_{2}+s_{1}+s_{0}$$ is odd, when $|seed|=(s_{31}\ldots s_1s_0)_2$. We can represent this linear combination conveniently by the hexadecimal number |0x01ecedc7|; the \.1 stands for $s_{24}$ and the final \.7 stands for $s_2+s_1+s_0$. The first ten least-significant bits turn out to be respectively |0x01ecedc7|, |0xdbbdc362|, |0x400e0b06|, |0x0eb73780|, |0xda0d66ae|, |0x002b63bc|, |0xadb801ed|, |0x8077bbbc|, |0x803d9db5|, and |0x401a0eda| in this notation (using the sign bit to indicate cases when 1 must be added to the sum). We must admit that these ten 32-bit patterns do not look at all random; the number of \.b's, \.d's, and \.0's is unusually high. (Before the ``warmup cycles,'' the patterns are even more regular.) This phenomenon eventually disappears, however, as the sequence proceeds; and it does not seem to imply any serious deficiency in practice, even at the beginning of the sequence, once we've done the warmup exercises. @= (void) gb_flip_cycle(); (void) gb_flip_cycle(); (void) gb_flip_cycle(); (void) gb_flip_cycle(); (void) gb_flip_cycle(); @ @(gb_flip.h@>= extern void gb_init_rand(); @* Uniform integers. Here is a simple routine that produces a uniform integer between 0 and~$m-1$, inclusive, when $m$ is any positive integer less than $2^{31}$. It avoids the bias toward small values that would occur if we simply calculated |gb_next_rand()%m|. (The bias is insignificant when |m| is small, but it can be serious when |m| is large. For example, if $m\approx 2^{32}\!/3$, the simple remainder algorithm would give an answer less than $m/2$ about 2/3 of the time.) This routine consumes fewer than two random numbers, on the average, for any fixed~$m$. In the \.{test\_flip} program (|main|), this routine should compute |t=m|, then it should reject the values |r=2081307921|, 1621414801, and 1469108743 before returning the answer 748103812. @d two_to_the_31 ((unsigned long)0x80000000) @= long gb_unif_rand(m) long m; {@+register unsigned long t=two_to_the_31-(two_to_the_31 % m); register long r; do@+{ r=gb_next_rand(); }@+while (t<=(unsigned long)r); return r%m; } @ @(gb_flip.h@>= extern long gb_unif_rand(); @* Index. Here is a list that shows where the identifiers of this program are defined and used. gb_graph.w0000444000175000017500000011245106156411614011147 0ustar dondon% This file is part of the Stanford GraphBase (c) Stanford University 1993 @i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES! \def\title{GB\_\,GRAPH} @* Introduction. This is {\sc GB\_\,GRAPH}, the data-structure module used by all GraphBase routines to allocate memory. The basic data types for graph representation are also defined~here. Many examples of how to use these conventions appear in other GraphBase modules. The best introduction to such examples can probably be found in {\sc GB\_\,BASIC}, which contains subroutines for generating and transforming various classical graphs. @ The code below is believed to be system-independent; it should produce equivalent results on all systems, assuming that the standard |calloc| and |free| functions of \CEE/ are available. However, a test program helps build confidence that everything does in fact work as it should. To make such a test, simply compile and run \.{test\_graph}. This particular test is fairly rudimentary, but it should be passed before more elaborate routines are tested. @(test_graph.c@>= #include "gb_graph.h" /* all users of {\sc GB\_\,GRAPH} should do this */ @@; @# int main() { @; @; @; printf("OK, the gb_graph routines seem to work!\n"); return 0; } @ The \CEE/ code for {\sc GB\_\,GRAPH} doesn't have a main routine; it's just a bunch of subroutines waiting to be incorporated into programs at a higher level via the system loading routine. Here is the general outline of \.{gb\_graph.c}: @p #ifdef SYSV #include #else #include #endif #include #include @h@# @@; @@; @@; @ @ The type declarations of {\sc GB\_\,GRAPH} appear also in the header file \.{gb\_graph.h}. For convenience, that header file also incorporates the standard system headers for input/output and string manipulation. Some system header files define an unsafe macro called |min|, which will interfere with GraphBase use of a useful identifier. We scotch that. @(gb_graph.h@>= #include #include #ifdef SYSV #include #else #include #endif #undef min @@; @ GraphBase programs often have a ``verbose'' option, which needs to be enabled by the setting of an external variable. They also tend to have a variable called |panic_code|, which helps identify unusual errors. We might as well declare those variables here. @= long verbose=0; /* nonzero if ``verbose'' output is desired */ long panic_code=0; /* set nonzero if graph generator returns null pointer */ @ Every external variable should be declared twice in this \.{CWEB} file: once for {\sc GB\_\,GRAPH} itself (the ``real'' declaration for storage allocation purposes) and once in \.{gb\_graph.h} (for cross-references by {\sc GB\_\,GRAPH} users). @(gb_graph.h@>= extern long verbose; /* nonzero if ``verbose'' output is desired */ extern long panic_code; /* set nonzero if graph generator panics */ @ When |panic_code| is assigned a nonzero value, one of the symbolic names defined here is used to help pinpoint the problem. Small values indicate memory limitations; values in the 10s and 20s indicate input/output anomalies; values in the 30s and 40s indicate errors in the parameters to a subroutine. Some panic codes stand for cases the author doesn't think will ever arise, although the program checks for them just to be extra safe. Multiple instances of the same type of error within a single subroutine are distinguished by adding an integer; for example, `|syntax_error+1|' and `|syntax_error+2|' identify two different kinds of syntax error, as an aid in trouble-shooting. The |early_data_fault| and |late_data_fault| codes are explained further by the value of |io_errors|. @(gb_graph.h@>= #define alloc_fault (-1) /* a previous memory request failed */ #define no_room 1 /* the current memory request failed */ #define early_data_fault 10 /* error detected at beginning of \.{.dat} file */ #define late_data_fault 11 /* error detected at end of \.{.dat} file */ #define syntax_error 20 /* error detected while reading \.{.dat} file */ #define bad_specs 30 /* parameter out of range or otherwise disallowed */ #define very_bad_specs 40 /* parameter far out of range or otherwise stupid */ #define missing_operand 50 /* graph parameter is |NULL| */ #define invalid_operand 60 /* graph parameter doesn't obey assumptions */ #define impossible 90 /* ``this can't happen'' */ @* Representation of graphs. The GraphBase programs employ a simple and flexible set of data structures to represent and manipulate graphs in computer memory. Vertices appear in a sequential array of \&{Vertex} records, and the arcs emanating from each vertex appear in a linked list of \&{Arc} records. There is also a \&{Graph} record, to provide information about the graph as a whole. The structure layouts for \&{Vertex}, \&{Arc}, and \&{Graph} records include a number of utility fields that can be used for any purpose by algorithms that manipulate the graphs. Each utility field is a union type that can be either a pointer of various kinds or a (long) integer. Let's begin the formal definition of these data structures by declaring the union type \&{util}. The suffixes .|V|, .|A|, .|G|, and .|S| on the name of a utility variable mean that the variable is a pointer to a vertex, arc, graph, or string, respectively; the suffix .|I| means that the variable is an integer. (We use one-character names because such names are easy to type when debugging.) @= typedef union { struct vertex_struct *V; /* pointer to \&{Vertex} */ struct arc_struct *A; /* pointer to \&{Arc} */ struct graph_struct *G; /* pointer to \&{Graph} */ char *S; /* pointer to string */ long I; /* integer */ } util; @ Each \&{Vertex} has two standard fields and six utility fields; hence it occupies 32 bytes on most systems, not counting the memory needed for supplementary string data. The standard fields are $$\vcenter{\halign{#,\ \ \hfil&#\hfil\cr |arcs|&a pointer to an \&{Arc};\cr |name|&a pointer to a string of characters.\cr}}$$ If |v| points to a \&{Vertex} and |v->arcs| is |NULL|, there are no arcs emanating from~|v|. But if |v->arcs| is non-|NULL|, it points to an \&{Arc} record representing an arc from~|v|, and that record has a |next| field that points in the same way to the representations of all other arcs from~|v|. The utility fields are called |u|, |v|, |w|, |x|, |y|, |z|. Macros can be used to give them syntactic sugar in particular applications. They are typically used to record such things as the in-degree or out-degree, or whether a vertex is `marked'. Utility fields might also link the vertex to other vertices or arcs in one or more lists. @= typedef struct vertex_struct { struct arc_struct *arcs; /* linked list of arcs coming out of this vertex */ char *name; /* string identifying this vertex symbolically */ util u,v,w,x,y,z; /* multipurpose fields */ } Vertex; @ Each \&{Arc} has three standard fields and two utility fields. Thus it occupies 20~bytes on most computer systems. The standard fields are $$\vcenter{\halign{#,\ \ \hfil&#\hfil\cr |tip|&a pointer to a |Vertex|;\cr |next|&a pointer to an \&{Arc};\cr |len|&a (long) integer.\cr}}$$ If |a| points to an \&{Arc} in the list of arcs from vertex~|v|, it represents an arc of length |a->len| from |v| to |a->tip|, and the next arc from |v| in the list is represented by |a->next|. The utility fields are called |a| and |b|. @= typedef struct arc_struct { struct vertex_struct *tip; /* the arc points to this vertex */ struct arc_struct *next; /* another arc pointing from the same vertex */ long len; /* length of this arc */ util a,b; /* multipurpose fields */ } Arc; @* Storage allocation. Memory space must be set aside dynamically for vertices, arcs, and their attributes. The GraphBase routines provided by {\sc GB\_\,GRAPH} accomplish this task with reasonable ease and efficiency by using the concept of memory ``areas.'' The user should first declare an \&{Area} variable by saying, for example, $$\hbox{\&{Area} |s|;}$$ and if this variable isn't static or otherwise known to be zero, it must be cleared initially by saying `|init_area(s)|'. Then any number of subroutine calls of the form `|gb_alloc(n,s)|' can be given; |gb_alloc| will return a pointer to a block of |n| consecutive bytes, all cleared to zero. Finally, the user can issue the command $$\hbox{|gb_free(s)|;}$$ this statement will return all memory blocks currently allocated to area~|s|, making them available for future allocation. The number of bytes |n| specified to |gb_alloc| must be positive, and it should usually be 1000 or more, since this will reduce the number of system calls. Other routines are provided below to allocate smaller amounts of memory, such as the space needed for a single new \&{Arc}. If no memory of the requested size is presently available, |gb_alloc| returns the null pointer |NULL|. In such cases |gb_alloc| also sets the external variable |gb_trouble_code| to a nonzero value. The user can therefore discover whether any one of an arbitrarily long series of allocation requests has failed by making a single test, `|if (gb_trouble_code)|'. The value of |gb_trouble_code| should be cleared to zero by every graph generation subroutine; therefore it need not be initialized to zero. A special macro |gb_typed_alloc(n,t,s)| makes it convenient to allocate the space for |n| items of type~|t| in area~|s|. @d gb_typed_alloc(n,t,s) @[(t*)@]gb_alloc((long)((n)*@[sizeof@](t)),s) @ The implementation of this scheme is almost ridiculously easy. The value of~|n| is increased by twice the number of bytes in a pointer, and the resulting number is rounded upwards if necessary so that it's a multiple of 256. Then memory is allocated using |calloc|. The extra bytes will contain two pointers, one to the beginning of the block and one to the next block associated with the same area variable. The \&{Area} type is defined to be an array of length 1. This makes it possible for users to say just `|s|' instead of `|&s|' when using an area variable as a parameter. @= #define init_area(s) @t\quad@> @[*s=NULL@] struct area_pointers { char *first; /* address of the beginning of this block */ struct area_pointers *next; /* address of area pointers in the previously allocated block */ }; typedef struct area_pointers *Area[1]; @ First we round |n| up, if necessary, so that it's a multiple of the size of a pointer variable. Then we know we can put |area_pointers| into memory at a position |n| after any address returned by |calloc|. (This logic should work whenever the number of bytes in a pointer variable is a divisor of~256.) The upper limit on |n| here is governed by old \CEE/ conventions in which the first parameter to |calloc| must be less than~$2^{16}$. Users who need graphs with more than half a million vertices might want to raise this limit on their systems, but they would probably be better off representing large graphs in a more compact way. {\sl Important Note:\/} Programs of the Stanford GraphBase implicitly assume that all memory allocated by |calloc| comes from a single underlying memory array. Pointer values are compared to each other in many places, even when the objects pointed to have been allocated at different times. Strictly speaking, this liberal use of pointer comparisons fails to conform to the restrictions of ANSI Standard \CEE/, if the comparison involves a less-than or greater-than relation. Users whose system supports only the strict standard will need to make several dozen changes. @^system dependencies@> @= char *gb_alloc(n,s) long n; /* number of consecutive bytes desired */ Area s; /* storage area that will contain the new block */ {@+long m=sizeof(char *); /* |m| is the size of a pointer variable */ Area t; /* a temporary pointer */ char *loc; /* the block address */ if (n<=0 || n>0xffff00-2*m) { gb_trouble_code|=2; /* illegal request */ return NULL; } n=((n+m-1)/m)*m; /* round up to multiple of |m| */ loc=(char*)calloc((unsigned)((n+2*m+255)/256),256); if (loc) { *t=(struct area_pointers*)(loc+n); (*t)->first=loc; (*t)->next=*s; *s=*t; }@+else gb_trouble_code|=1; return loc; } @ @= long gb_trouble_code=0; /* did |gb_alloc| return |NULL|? */ @ @(gb_graph.h@>= extern long gb_trouble_code; /* anomalies noted by |gb_alloc| */ @ Notice that |gb_free(s)| can be called twice in a row, because the list of blocks is cleared out of the area variable~|s|. @= void gb_free(s) Area s; {@+Area t; while (*s) { *t=(*s)->next; free((*s)->first); *s=*t; } } @ The two external procedures we've defined above should be mentioned in the header file, so let's do that before we forget. @(gb_graph.h@>= extern char *gb_alloc(); /* allocate another block for an area */ #define gb_typed_alloc(n,t,s) @[@t\quad@>\ @[(t*)@]gb_alloc((long)((n)*@[sizeof@](t)),s)@] extern void gb_free(); /* deallocate all blocks for an area */ @ Here we try to allocate 10 million bytes of memory. If we succeed, fine; if not, we verify that the error was properly reported. (An early draft of this program attempted to allocate memory until all space was exhausted. That tactic provided a more thorough test, but it was a bad idea because it brought certain large systems to their knees; it was terribly unfriendly to other users who were innocently trying to do their own work on the same machine.) @= if (gb_alloc(0L,s)!=NULL || gb_trouble_code!=2) { fprintf(stderr,"Allocation error 2 wasn't reported properly!\n");@+return -2; } for (;g->vv.I<100;g->vv.I++)@+ if (gb_alloc(100000L,s)) { g->uu.I++; printf("."); fflush(stdout); } if (g->uu.I<100 && gb_trouble_code!=3) { fprintf(stderr,"Allocation error 1 wasn't reported properly!\n");@+return -1; } if (g->uu.I==0) { fprintf(stderr,"I couldn't allocate any memory!\n");@+return -3; } gb_free(s); /* we've exhausted memory, let's put some back */ printf("Hey, I allocated %ld00000 bytes successfully. Terrific...\n",g->uu.I); gb_trouble_code=0; @ @= Area s; /* temporary allocations in the test routine */ @*Growing a graph. Now we're ready to look at the \&{Graph} type. This is a data structure that can be passed to an algorithm that operates on graphs---to find minimum spanning trees, or strong components, or whatever. A \&{Graph} record has seven standard fields and six utility fields. The standard fields are $$\vcenter{\halign{#,\ \ \hfil&#\hfil\cr |vertices|&a pointer to an array of |Vertex| records;\cr |n|&the total number of vertices;\cr |m|&the total number of arcs;\cr |id|&a symbolic identification giving parameters of the GraphBase procedure\cr \omit& that generated this graph;\cr |util_types|&a symbolic representation of the data types in utility fields;\cr |data|&an |Area| used for |Arc| storage and string storage;\cr |aux_data|&an |Area| used for auxiliary information that some users might\cr \omit &want to discard.\cr}}$$ The utility fields are called |uu|, |vv|, |ww|, |xx|, |yy|, and |zz|. As a consequence of these conventions, we can visit all arcs of a graph~|g| by using the following program: $$\vcenter{\halign{#\hfil\cr |Vertex *v;|\cr |Arc *a;|\cr |for (v=g->vertices; vvertices+g->n; v++)|\cr \quad|for (a=v->arcs; a; a=a->next)|\cr \qquad\\{visit}|(v,a)|;\cr}}$$ @= #define ID_FIELD_SIZE 161 typedef struct graph_struct { Vertex *vertices; /* beginning of the vertex array */ long n; /* total number of vertices */ long m; /* total number of arcs */ char id[ID_FIELD_SIZE]; /* GraphBase identification */ char util_types[15]; /* usage of utility fields */ Area data; /* the main data blocks */ Area aux_data; /* subsidiary data blocks */ util uu,vv,ww,xx,yy,zz; /* multipurpose fields */ } Graph; @ The |util_types| field should always hold a string of length 14, followed as usual by a null character to terminate that string. The first six characters of |util_types| specify the usage of utility fields |u|, |v|, |w|, |x|, |y|, and~|z| in |Vertex| records; the next two characters give the format of the utility fields in |Arc| records; the last six give the format of the utility fields in |Graph| records. Each character should be either \.I (denoting a |long| integer), \.S (denoting a pointer to a string), \.V (denoting a pointer to a |Vertex|), \.A (denoting a pointer to an |Arc|), \.G (denoting a pointer to a |Graph|), or \.Z (denoting an unused field that remains zero). The default for |util_types| is |"ZZZZZZZZZZZZZZ"|, when none of the utility fields is being used. For example, suppose that a bipartite graph |g| is using field |g->uu.I| to specify the size of its first part. Suppose further that |g| has a string in utility field |a| of each |Arc| and uses utility field |w| of |Vertex| records to point to an |Arc|. If |g| leaves all other utility fields untouched, its |util_types| should be |"ZZAZZZSZIZZZZZ"|. The |util_types| string is presently examined only by the |save_graph| and |restore_graph| routines, which convert GraphBase graphs from internal data structures to symbolic external files and vice versa. Therefore users need not update the |util_types| when they write algorithms to manipulate graphs, unless they are going to use |save_graph| to output a graph in symbolic form, or unless they are using some other GraphBase-related software that might rely on the conventions of |util_types|. (Such software is not part of the ``official'' Stanford GraphBase, but it might conceivably exist some~day.) @ Some applications of bipartite graphs require all vertices of the first part to appear at the beginning of the |vertices| array. In such cases, utility field |uu.I| is traditionally given the symbolic name |n_1|, and it is set equal to the size of that first part. The size of the other part is then |g->n - g->n_1|. @^bipartite graph@> @d n_1 uu.I /* utility field |uu| may denote size of bipartite first part */ @(gb_graph.h@>= #define n_1 @t\quad@> uu.I #define mark_bipartite(g,n1) @[g->n_1=n1,g->util_types[8]='I'@] @ A new graph is created by calling |gb_new_graph(n)|, which returns a pointer to a |Graph| record for a graph with |n| vertices and no arcs. This function also initializes several private variables that are used by the |gb_new_arc|, |gb_new_edge|, |gb_virgin_arc|, and |gb_save_string| procedures below. We actually reserve space for |n+extra_n| vertices, although claiming only~$n$, because several graph manipulation algorithms like to add a special vertex or two to the graphs they deal with. @= Graph *gb_new_graph(n) long n; /* desired number of vertices */ { cur_graph=(Graph*)calloc(1,sizeof(Graph)); if (cur_graph) { cur_graph->vertices=gb_typed_alloc(n+extra_n,Vertex,cur_graph->data); if (cur_graph->vertices) {Vertex *p; cur_graph->n=n; for (p=cur_graph->vertices+n+extra_n-1; p>=cur_graph->vertices; p--) p->name=null_string; sprintf(cur_graph->id,"gb_new_graph(%ld)",n); strcpy(cur_graph->util_types,"ZZZZZZZZZZZZZZ"); }@+else { free((char*)cur_graph); cur_graph=NULL; } } next_arc=bad_arc=NULL; next_string=bad_string=NULL; gb_trouble_code=0; return cur_graph; } @ The value of |extra_n| is ordinarily~4, and it should probably always be at least~4. @= long extra_n=4; /* the number of shadow vertices allocated by |gb_new_graph| */ char null_string[1]; /* a null string constant */ @ @(gb_graph.h@>= extern long extra_n; /* the number of shadow vertices allocated by |gb_new_graph| */ extern char null_string[]; /* a null string constant */ extern void make_compound_id(); /* routine to set one |id| field from another */ extern void make_double_compound_id(); /* ditto, but from two others */ @ The |id| field of a graph is sometimes manufactured from the |id| field of another graph. The following routines do this without allowing the string to get too long after repeated copying. @= void make_compound_id(g,s1,gg,s2) /* |sprintf(g->id,"%s%s%s",s1,gg->id,s2)| */ Graph *g; /* graph whose |id| is to be set */ char *s1; /* string for the beginning of the new |id| */ Graph *gg; /* graph whose |id| is to be copied */ char *s2; /* string for the end of the new |id| */ {@+int avail=ID_FIELD_SIZE-strlen(s1)-strlen(s2); char tmp[ID_FIELD_SIZE]; strcpy(tmp,gg->id); if (strlen(tmp)id,"%s%s%s",s1,tmp,s2); else sprintf(g->id,"%s%.*s...)%s",s1,avail-5,tmp,s2); } @ @= void make_double_compound_id(g,s1,gg,s2,ggg,s3) /* |sprintf(g->id,"%s%s%s%s%s",s1,gg->id,s2,ggg->id,s3)| */ Graph *g; /* graph whose |id| is to be set */ char *s1; /* string for the beginning of the new |id| */ Graph *gg; /* first graph whose |id| is to be copied */ char *s2; /* string for the middle of the new |id| */ Graph *ggg; /* second graph whose |id| is to be copied */ char *s3; /* string for the end of the new |id| */ {@+int avail=ID_FIELD_SIZE-strlen(s1)-strlen(s2)-strlen(s3); if (strlen(gg->id)+strlen(ggg->id)id,"%s%s%s%s%s",s1,gg->id,s2,ggg->id,s3); else sprintf(g->id,"%s%.*s...)%s%.*s...)%s",s1,avail/2-5,gg->id, s2,(avail-9)/2,ggg->id,s3); } @ But how do the arcs get there? That's where the private variables in |gb_new_graph| come in. If |next_arc| is unequal to |bad_arc|, it points to an unused |Arc| record in a previously allocated block of |Arc| records. Similarly, |next_string| and |bad_string| are addresses used to place strings into a block of memory allocated for that purpose. @= static Arc *next_arc; /* the next |Arc| available for allocation */ static Arc *bad_arc; /* but if |next_arc=bad_arc|, that |Arc| isn't there */ static char *next_string; /* the next byte available for storing a string */ static char *bad_string; /* but if |next_string=bad_string|, don't byte */ static Arc dummy_arc[2]; /* an |Arc| record to point to in an emergency */ static Graph dummy_graph; /* a |Graph| record that's normally unused */ static Graph *cur_graph=&dummy_graph; /* the |Graph| most recently created */ @ All new |Arc| records that are created by the automatic |next_arc|/|bad_arc| scheme originate in a procedure called |gb_virgin_arc|, which returns the address of a new record having type |Arc|. When a new block of |Arc| records is needed, we create 102 of them at once. This strategy causes exactly 2048 bytes to be allocated on most computer systems---a nice round number. The routine will still work, however, if 102 is replaced by any positive even number. The new block goes into the |data| area of |cur_graph|. Graph-building programs do not usually call |gb_virgin_arc| directly; they generally invoke one of the higher-level routines |gb_new_arc| or |gb_new_edge| described below. If memory space has been exhausted, |gb_virgin_arc| will return a pointer to |dummy_arc|, so that the calling procedure can safely refer to fields of the result even though |gb_trouble_code| is nonzero. @d arcs_per_block 102 @= Arc *gb_virgin_arc() {@+register Arc *cur_arc=next_arc; if (cur_arc==bad_arc) { cur_arc=gb_typed_alloc(arcs_per_block,Arc,cur_graph->data); if (cur_arc==NULL) cur_arc=dummy_arc; else { next_arc = cur_arc+1; bad_arc = cur_arc+arcs_per_block; } } else next_arc++; return cur_arc; } @ The routine |gb_new_arc(u,v,len)| creates a new arc of length |len| from vertex~|u| to vertex~|v|. The arc becomes part of the graph that was most recently created by |gb_new_graph|---the graph pointed to by the private variable |cur_graph|. This routine assumes that |u| and |v| are both vertices in |cur_graph|. The new arc will be pointed to by |u->arcs|, immediately after |gb_new_arc(u,v,len)| has acted. If there is no room for the new arc, |gb_trouble_code| is set nonzero, but |u->arcs| will point to the non-|NULL| record |dummy_arc| so that additional information can safely be stored in its utility fields without risking system crashes before |gb_trouble_code| is tested. However, the linking structure of arcs is apt to be fouled up in such cases; programs should make sure that |gb_trouble_code==0| before doing any extensive computation on a graph. @= void gb_new_arc(u,v,len) Vertex *u, *v; /* a newly created arc will go from |u| to |v| */ long len; /* its length */ {@+register Arc *cur_arc=gb_virgin_arc(); cur_arc->tip=v; @+cur_arc->next=u->arcs; @+cur_arc->len=len; u->arcs=cur_arc; cur_graph->m++; } @ An undirected graph has ``edges'' instead of arcs. We represent an edge by two arcs, one going each way. @^undirected graph@> The fact that |arcs_per_block| is even means that the |gb_new_edge| routine needs to call |gb_virgin_arc| only once instead of twice. Caveats: This routine, like |gb_new_arc|, should be used only after |gb_new_graph| has caused the private variable |cur_graph| to point to the graph containing the new edge. The routine |gb_new_edge| must not be used together with |gb_new_arc| or |gb_virgin_arc| when building a graph, unless |gb_new_arc| and |gb_virgin_arc| have been called an even number of times before |gb_new_edge| is invoked. The new edge will be pointed to by |u->arcs| and by |v->arcs| immediately after |gb_new_edge| has created it, assuming that |u!=v|. The two arcs appear next to each other in memory; indeed, |gb_new_edge| rigs things so that |v->arcs| is |u->arcs+1| when |unext=a+1|; it will be arc |a-1| if and only if |u>=v| and |a->next!=a+1|. The condition |a->next=a+1| can hold only if |u=v|. @d gb_new_graph gb_nugraph /* abbreviations for Procrustean linkers */ @d gb_new_arc gb_nuarc @d gb_new_edge gb_nuedge @= void gb_new_edge(u,v,len) Vertex *u, *v; /* new arcs will go from |u| to |v| and from |v| to |u| */ long len; /* their length */ {@+register Arc *cur_arc=gb_virgin_arc(); if (cur_arc!=dummy_arc) next_arc++; if (utip=v; @+cur_arc->next=u->arcs; (cur_arc+1)->tip=u; @+(cur_arc+1)->next=v->arcs; u->arcs=cur_arc; v->arcs=cur_arc+1; }@+else { (cur_arc+1)->tip=v; @+(cur_arc+1)->next=u->arcs; u->arcs=cur_arc+1; /* do this now in case |u==v| */ cur_arc->tip=u; @+cur_arc->next=v->arcs; v->arcs=cur_arc; } cur_arc->len=(cur_arc+1)->len=len; cur_graph->m+=2; } @ Sometimes (let us hope rarely) we might need to use a dirty trick hinted at in the previous discussion. On most computers, the mate to arc~|a| will be |a-1| if and only if |edge_trick&(siz_t)a| is nonzero. @^system dependencies@> @^pointer hacks@> @= siz_t edge_trick=sizeof(Arc)-(sizeof(Arc)&(sizeof(Arc)-1)); @ @(gb_graph.h@>= extern siz_t edge_trick; /* least significant 1 bit in |sizeof(Arc)| */ @ The type |siz_t| just mentioned should be the type returned by \CEE/'s |sizeof| operation; it's the basic unsigned type for machine addresses in pointers. ANSI standard \CEE/ calls this type \&{size\_t}, but we cannot safely use \&{size\_t} in all the GraphBase programs because some older \CEE/ systems mistakenly define \&{size\_t} to be a signed type. @^system dependencies@> @f siz_t int @= typedef unsigned long @[siz_t@]; /* basic machine address, as signless integer */ @ Vertices generally have a symbolic name, and we need a place to put such names. The |gb_save_string| function is a convenient utility for this purpose: Given a null-terminated string of any length, |gb_save_string| stashes it away in a safe place and returns a pointer to that place. Memory is conserved by combining strings from the current graph into largish blocks of a convenient size. Note that |gb_save_string| should be used only after |gb_new_graph| has provided suitable initialization, because the private variable |cur_graph| must point to the graph for which storage is currently being allocated, and because the private variables |next_string| and |bad_string| must also have suitable values. @d string_block_size 1016 /* $1024-8$ is usually efficient */ @= char *gb_save_string(s) register char *s; /* the string to be copied */ {@+register char *p=s; register long len; /* length of the string and the following null character */ while (*p++) ; /* advance to the end of the string */ len=p-s; p=next_string; if (p+len>bad_string) { /* not enough room in the current block */ long size=string_block_size; if (len>size) size=len; p=gb_alloc(size,cur_graph->data); if (p==NULL) return null_string; /* return a pointer to |""| if memory ran out */ bad_string=p+size; } while (*s) *p++=*s++; /* copy the non-null bytes of the string */ *p++='\0'; /* and append a null character */ next_string=p; return p-len; } @ The test routine illustrates some of these basic maneuvers. @= g=gb_new_graph(2L); if (g==NULL) { fprintf(stderr,"Oops, I couldn't even create a trivial graph!\n"); return -4; } u=g->vertices;@+ v=u+1; u->name=gb_save_string("vertex 0"); v->name=gb_save_string("vertex 1"); @ @= Graph *g; Vertex *u,*v; @ If the ``edge trick'' fails, the standard GraphBase routines are unaffected except for the demonstration program {\sc MILES\_\,SPAN}. (And that program uses |edge_trick| only when printing verbose comments.) @^edge trick failure@> @= if (strncmp(u->name,v->name,7)) { fprintf(stderr,"Something is fouled up in the string storage machinery!\n"); return -5; } gb_new_edge(v,u,-1L); gb_new_edge(u,u,1L); gb_new_arc(v,u,-1L); if ((edge_trick&(siz_t)(u->arcs))|| (edge_trick&(siz_t)(u->arcs->next->next))|| !(edge_trick&(siz_t)(v->arcs->next))) printf("Warning: The \"edge trick\" failed!\n"); if (v->name[7]+g->n!=v->arcs->next->tip->name[7]+g->m-2) { /* |'1'+2!='0'+5-2| */ fprintf(stderr,"Sorry, the graph data structures aren't working yet.\n"); return -6; } @ Some applications might need to add arcs to several graphs at a time, violating the assumptions stated above about |cur_graph| and the other private variables. The |switch_to_graph| function gets around that restriction, by using the utility slots |ww|, |xx|, |yy|, and |zz| of |Graph| records to save and restore the private variables. Just say |switch_to_graph(g)| in order to make |cur_graph| be~|g| and to restore the other private variables that are needed by |gb_new_arc|, |gb_virgin_arc|, |gb_new_edge|, and |gb_save_string|. Restriction: The graph |g| being switched to must have previously been switched from; that is, it must have been |cur_graph| when |switch_to_graph| was called previously. Otherwise its private allocation variables will not have been saved. To meet this restriction, you should say |switch_to_graph(NULL)| just before calling |gb_new_graph|, if you intend to switch back to the current graph later. (The swap-in-swap-out nature of these conventions may seem inelegant, but convenience and efficiency are more important than elegance when most applications do not need the ability to switch between graphs.) @= void switch_to_graph(g) Graph *g; { cur_graph->ww.A=next_arc; @+cur_graph->xx.A=bad_arc; cur_graph->yy.S=next_string; @+cur_graph->zz.S=bad_string; cur_graph=(g? g: &dummy_graph); next_arc=cur_graph->ww.A; @+bad_arc=cur_graph->xx.A; next_string=cur_graph->yy.S; @+bad_string=cur_graph->zz.S; cur_graph->ww.A=NULL; cur_graph->xx.A=NULL; cur_graph->yy.S=NULL; cur_graph->zz.S=NULL; } @ Finally, here's a routine that obliterates an entire graph when it is no longer needed: @= void gb_recycle(g) Graph *g; { if (g) { gb_free(g->data); gb_free(g->aux_data); free((char*)g); /* the user must not refer to |g| again */ } } @ @(gb_graph.h@>= #define gb_new_graph gb_nugraph /* abbreviations for external linkage */ #define gb_new_arc gb_nuarc #define gb_new_edge gb_nuedge extern Graph*gb_new_graph(); /* create a new graph structure */ extern void gb_new_arc(); /* append an arc to the current graph */ extern Arc*gb_virgin_arc(); /* allocate a new |Arc| record */ extern void gb_new_edge(); /* append an edge (two arcs) to the current graph */ extern char*gb_save_string(); /* store a string in the current graph */ extern void switch_to_graph(); /* save allocation variables, swap in others */ extern void gb_recycle(); /* delete a graph structure */ @* Searching for vertices. We sometimes want to be able to find a vertex, given its name, and it is nice to do this in a standard way. The following simple subroutines can be used: {\narrower \smallskip|hash_in(v)| puts the name of vertex |v| into the hash table; \smallskip|hash_out(s)| finds a vertex named |s|, if present in the hash table; \smallskip|hash_setup(g)| prepares a hash table for all vertices of graph~|g|; \smallskip|hash_lookup(s,g)| looks up the name |s| in the hash table of |g|. \smallskip} \noindent Routines |hash_in| and |hash_out| apply to the current graph being created, while |hash_setup| and |hash_lookup| apply to arbitrary graphs. Important: Utility fields |u| and |v| of each vertex are reserved for use by the search routine when hashing is active. You can crash the system if you try to fool around with these values yourself, or if you use any subroutines that change those fields. The first two characters in the current graph's |util_types| field should be \.{VV} if the hash table information is to be saved by {\sc GB\_\,SAVE}. Warning: Users of this hash scheme must preserve the number of vertices |g->n| in the current graph~|g|. If |g->n| is changed, the hash table will be worthless, unless |hash_setup| is used to rehash everything. @= extern void hash_in(); /* input a name to the hash table of current graph */ extern Vertex* hash_out(); /* find a name in hash table of current graph */ extern void hash_setup(); /* create a hash table for a given graph */ extern Vertex* hash_lookup(); /* find a name in a given graph */ @ The lookup scheme is quite simple. We compute a more-or-less random value |h| based on the vertex name, where |0<=hvertices+h)->hash_head| and linked together in the |hash_link| fields, where |hash_head| and |hash_link| are utility fields |u.V| and |v.V|. @d hash_link u.V @d hash_head v.V @ @= void hash_in(v) Vertex *v; {@+ register char *t=v->name; register Vertex *u; @; v->hash_link=u->hash_head; u->hash_head=v; } @ The hash code for a string $c_1c_2\ldots c_l$ of length $l$ is a nonlinear function of the characters; this function appears to produce reasonably random results between 0 and the number of vertices in the current graph. Simpler approaches were noticeably poorer in the author's tests. Caution: This hash coding scheme is system-dependent, because it uses the system's character codes. If you create a graph on a machine with ASCII code and save it with {\sc GB\_\,SAVE}, and if you subsequently ship the resulting text file to some friend whose machine does not use ASCII code, your friend will have to rebuild the hash structure with |hash_setup| before being able to use |hash_lookup| successfully. @^character-set dependencies@> @d HASH_MULT 314159 /* random multiplier */ @d HASH_PRIME 516595003 /* the 27182818th prime; it's less than $2^{29}$ */ @= {@+register long h; for (h=0;*t;t++) { h+=(h^(h>>1))+HASH_MULT*(unsigned char)*t; while (h>=HASH_PRIME) h-=HASH_PRIME; } u=cur_graph->vertices+(h % cur_graph->n); } @ If the hash function were truly random, the average number of string comparisons made would be less than $(e^2+7)/8\approx 1.80$ on a successful search, and less than $(e^2+1)/4\approx2.10$ on an unsuccessful search [{\sl Sorting and Searching}, Section 6.4, Eqs.~(15) and~(16)]. @= Vertex* hash_out(s) char* s; {@+register char *t=s; register Vertex *u; @; for (u=u->hash_head;u;u=u->hash_link) if (strcmp(s,u->name)==0) return u; return NULL; /* not found */ } @ @= void hash_setup(g) Graph *g; {@+Graph *save_cur_graph; if (g && g->n>0) {@+register Vertex *v; save_cur_graph=cur_graph; cur_graph=g; for (v=g->vertices;vvertices+g->n;v++) v->hash_head=NULL; for (v=g->vertices;vvertices+g->n;v++) hash_in(v); g->util_types[0]=g->util_types[1]='V'; /* indicate usage of |hash_head| and |hash_link| */ cur_graph=save_cur_graph; } } @ @= Vertex* hash_lookup(s,g) char *s; Graph *g; {@+Graph *save_cur_graph; if (g && g->n>0) {@+register Vertex *v; save_cur_graph=cur_graph; cur_graph=g; v=hash_out(s); cur_graph=save_cur_graph; return v; } else return NULL; } @* Index. Here is a list that shows where the identifiers of this program are defined and used. gb_io.w0000444000175000017500000005222306256064252010460 0ustar dondon% This file is part of the Stanford GraphBase (c) Stanford University 1993 @i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES! \def\title{GB\_\,IO} @* Introduction. This is {\sc GB\_\,IO}, the input/output module used by all GraphBase routines to access data~files. It doesn't actually do any output; but somehow `input/output' sounds like a more useful title than just `input'. All files of GraphBase data are designed to produce identical results on almost all existing computers and operating systems. Each line of each file contains at most 79 characters. Each character is either a blank or a digit or an uppercase letter or a lowercase letter or a standard punctuation mark. Blank characters at the end of each line are ``invisible''; that is, they have no perceivable effect. Hence identical results will be obtained on record-oriented systems that pad every line with blanks. The data is carefully sum-checked so that defective input files have little chance of being accepted. @ Changes might be needed when these routines are ported to different systems. Sections of the program that are most likely to require such changes are listed under `system dependencies' in the index. A validation program is provided so that installers can tell if {\sc GB\_\,IO} is working properly. To make the test, simply run \.{test\_io}. @(test_io.c@>= #include "gb_io.h" /* all users of {\sc GB\_\,IO} should include this header file */ #define exit_test(m) /* we invoke this macro if something goes wrong */\ {@+fprintf(stderr,"%s!\n(Error code = %ld)\n",m,io_errors);@+return -1;@+} @t\2@>@/ int main() { @; @; @; printf("OK, the gb_io routines seem to work!\n"); return 0; } @ The external variable |io_errors| mentioned in the previous section will be set nonzero if any anomalies are detected. Errors won't occur in normal use of GraphBase programs, so no attempt has been made to provide a user-friendly way to decode the nonzero values that |io_errors| might assume. Information is simply gathered in binary form; system wizards who might need to do a bit of troubleshooting should be able to decode |io_errors| without great pain. @d cant_open_file 0x1 /* bit set in |io_errors| if |fopen| fails */ @d cant_close_file 0x2 /* bit set if |fclose| fails */ @d bad_first_line 0x4 /* bit set if the data file's first line isn't legit */ @d bad_second_line 0x8 /* bit set if the second line doesn't pass muster */ @d bad_third_line 0x10 /* bit set if the third line is awry */ @d bad_fourth_line 0x20 /* guess when this bit is set */ @d file_ended_prematurely 0x40 /* bit set if |fgets| fails */ @d missing_newline 0x80 /* bit set if line is too long or |'\n'| is missing */ @d wrong_number_of_lines 0x100 /* bit set if the line count is wrong */ @d wrong_checksum 0x200 /* bit set if the checksum is wrong */ @d no_file_open 0x400 /* bit set if user tries to close an unopened file */ @d bad_last_line 0x800 /* bit set if final line has incorrect form */ @ The \CEE/ code for {\sc GB\_\,IO} doesn't have a main routine; it's just a bunch of subroutines to be incorporated into programs at a higher level via the system loading routine. Here is the general outline of \.{gb\_io.c}: @p @
@; @h @@; @@; @@; @ @ Every external variable is declared twice in this \.{CWEB} file: once for {\sc GB\_\,IO} itself (the ``real'' declaration for storage allocation purposes) and once in \.{gb\_io.h} (for cross-references by {\sc GB\_\,IO} users). @= long io_errors; /* record of anomalies noted by {\sc GB\_\,IO} routines */ @ @(gb_io.h@>= @@; extern long io_errors; /* record of anomalies noted by {\sc GB\_\,IO} routines */ @ We will stick to standard \CEE/-type input conventions. We'll also have occasion to use some of the standard string operations. @= #include #ifdef SYSV #include #else #include #endif @* Inputting a line. The {\sc GB\_\,IO} routines get their input from an array called |buffer|. This array is internal to {\sc GB\_\,IO}---its contents are hidden from user programs. We make it 81 characters long, since the data is supposed to have at most 79 characters per line, followed by newline and null. @= static char buffer[81]; /* the current line of input */ static char *cur_pos=buffer; /* the current character of interest */ static FILE *cur_file; /* current file, or |NULL| if none is open */ @ Here's a basic subroutine to fill the |buffer|. The main feature of interest is the removal of trailing blanks. We assume that |cur_file| is open. Notice that a line of 79 characters (followed by |'\n'|) will just fit into the buffer, and will cause no errors. A line of 80 characters will be split into two lines and the |missing_newline| message will occur, because of the way |fgets| is defined. A |missing_newline| error will also occur if the file ends in the middle of a line, or if a null character (|'\0'|) occurs within a line. @= static void fill_buf() {@+register char *p; if (!fgets(buffer,sizeof(buffer),cur_file)) { io_errors |= file_ended_prematurely; buffer[0]=more_data=0; } for (p=buffer; *p; p++) ; /* advance to first null character */ if (p--==buffer || *p!='\n') { io_errors |= missing_newline; p++; } while (--p>=buffer && *p==' ') ; /* move back over trailing blanks */ *++p='\n'; *++p=0; /* newline and null are always present at end of line */ cur_pos=buffer; /* get ready to read |buffer[0]| */ } @* Checksums. Each data file has a ``magic number,'' which is defined to be $$\biggl(\sum_l 2^l c_l\biggr) \bmod p\,.$$ Here $p$ is a large prime number, and $c_l$ denotes the internal code corresponding to the $l$th-from-last data character read (including newlines but not nulls). The ``internal codes'' $c_l$ are computed in a system-independent way: Each character |c| in the actual encoding scheme being used has a corresponding |icode|, which is the same on all systems. For example, the |icode| of |'0'| is zero, regardless of whether |'0'| is actually represented in ASCII or EBCDIC or some other scheme. (We assume that every modern computer system is capable of printing at least 95 different characters, including a blank space.) We will accept a data file as error-free if it has the correct number of lines and ends with the proper magic number. @= static char icode[256]; /* mapping of characters to internal codes */ static long checksum_prime=(1L<<30)-83; /* large prime such that $2p+|unexpected_char|$ won't overflow */ static long magic; /* current checksum value */ static long line_no; /* current line number in file */ static long final_magic; /* desired final magic number */ static long tot_lines; /* total number of data lines */ static char more_data; /* is there data still waiting to be read? */ @ The |icode| mapping is defined by a single string, |imap|, such that character |imap[k]| has |icode| value~|k|. There are 96 characters in |imap|, namely the 94 standard visible ASCII codes plus space and newline. If EBCDIC code is used instead of ASCII, the cents sign \rlap{\.{\kern.05em/}}\.c should take the place of single-left-quote \.{\char`\`}, and \.{\char5}~should take the place of\/~\.{\char`\~}. All characters that don't appear in |imap| are given the same |icode| value, called |unexpected_char|. Such characters should be avoided in GraphBase files whenever possible. (If they do appear, they can still get into a user's data, but we don't distinguish them from each other for checksumming purposes.) The |icode| table actually plays a dual role, because we've rigged it so that codes 0--15 come from the characters |"0123456789ABCDEF"|. This facilitates conversion of decimal and hexadecimal data. We can also use it for radices higher than 16. @d unexpected_char 127 /* default |icode| value */ @= static char *imap="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz_^~&@@,;.:?!%#$+-*/|\\<=>()[]{}`'\" \n"; @ Users of {\sc GB\_\,IO} can look at the |imap|, but they can't change it. @= char imap_chr(d) long d; { return d<0 || d>strlen(imap)? '\0': imap[d]; } @# long imap_ord(c) char c; { @; return (c<0||c>255)? unexpected_char: icode[c]; } @ @(gb_io.h@>= #define unexpected_char @t\quad@> 127 extern char imap_chr(); /* the character that maps to a given character */ extern long imap_ord(); /* the ordinal number of a given character */ @ @= if (!icode['1']) icode_setup(); @ @= static void icode_setup() {@+register long k; register char *p; for (k=0;k<256;k++) icode[k]=unexpected_char; for (p=imap,k=0; *p; p++,k++) icode[*p]=k; } @ Now we're ready to specify some external subroutines that do input. Calling |gb_newline()| will read the next line of data into |buffer| and update the magic number accordingly. @(gb_io.h@>= extern void gb_newline(); /* advance to next line of the data file */ extern long new_checksum(); /* compute change in magic number */ @ Users can compute checksums as |gb_newline| does, but they can't change the (private) value of |magic|. @= long new_checksum(s,old_checksum) char *s; /* a string */ long old_checksum; {@+register long a=old_checksum; register char*p; for (p=s; *p; p++) a=(a+a+imap_ord(*p)) % checksum_prime; return a; } @ The magic checksum is not affected by lines that begin with \.*. @= void gb_newline() { if (++line_no>tot_lines) more_data=0; if (more_data) { fill_buf(); if (buffer[0]!='*') magic=new_checksum(buffer,magic); } } @ Another simple routine allows a user to read (but not write) the variable |more_data|. @(gb_io.h@>= extern long gb_eof(); /* has the data all been read? */ @ @= long gb_eof() { return !more_data; } @* Parsing a line. The user can input characters from the buffer in several ways. First, there's a basic |gb_char()| routine, which returns a single character. The character is |'\n'| if the last character on the line has already been read (and it continues to be |'\n'| until the user calls |gb_newline|). The current position in the line, |cur_pos|, always advances when |gb_char| is called, unless |cur_pos| was already at the end of the line. There's also a |gb_backup()| routine, which moves |cur_pos| one place to the left unless it was already at the beginning. @(gb_io.h@>= extern char gb_char(); /* get next character of current line, or |'\n'| */ extern void gb_backup(); /* move back ready to scan a character again */ @ @= char gb_char() { if (*cur_pos) return (*cur_pos++); return '\n'; } @# void gb_backup() { if (cur_pos>buffer) cur_pos--; } @ There are two ways to read numerical data. The first, |gb_digit(d)|, expects to read a single character in radix~|d|, using |icode| values to specify digits greater than~9. (Thus, for example, |'A'| represents the hexadecimal digit for decimal~10.) If the next character is a valid |d|-git, |cur_pos| moves to the next character and the numerical value is returned. Otherwise |cur_pos| stays in the same place and $-1$ is returned. The second routine, |gb_number(d)|, reads characters and forms an unsigned radix-|d| number until the first non-digit is encountered. The resulting number is returned; it is zero if no digits were found. No errors are possible with this routine, because it uses |unsigned long| arithmetic. @(gb_io.h@>= extern long gb_digit(); /* |gb_digit(d)| reads a digit between 0 and |d-1| */ extern unsigned long gb_number(); /* |gb_number(d)| reads a radix-|d| number */ @ The value of |d| should be at most 127, if users want their programs to be portable, because \CEE/ does not treat larger |char| values in a well-defined manner. In most applications, |d| is of course either 10 or 16. @= long gb_digit(d) char d; { icode[0]=d; /* make sure |'\0'| is a nondigit */ if (imap_ord(*cur_pos)= #define STR_BUF_LENGTH 160 extern char str_buf[]; /* safe place to receive output of |gb_string| */ extern char *gb_string(); /* |gb_string(p,c)| reads a string delimited by |c| into bytes starting at |p| */ @ @d STR_BUF_LENGTH 160 @= char str_buf[STR_BUF_LENGTH]; /* users can put strings here if they wish */ char *gb_string(p,c) char *p; /* where to put the result */ char c; /* character following the string */ { while (*cur_pos && *cur_pos!=c) *p++=*cur_pos++; *p++=0; return p; } @ Here's how we test those routines in \.{test\_io}: The first line of test data consists of 79 characters, beginning with 64 zeroes and ending with `\.{123456789ABCDEF}'. The second line is completely blank. The third and final line says `\.{Oops:(intentional mistake)}'. @= if (gb_number(10)!=123456789) io_errors |= 1L<<20; /* decimal number not working */ if (gb_digit(16)!=10) io_errors |= 1L<<21; /* we missed the \.A following the decimal number */ gb_backup();@+ gb_backup(); /* get set to read `\.{9A}' again */ if (gb_number(16)!=0x9ABCDEF) io_errors |= 1L<<22; /* hexadecimal number not working */ gb_newline(); /* now we should be scanning a blank line */ if (gb_char()!='\n') io_errors |= 1L<<23; /* newline not inserted at end */ if (gb_char()!='\n') io_errors |= 1L<<24; /* newline not implied after end */ if (gb_number(60)!=0) io_errors |= 1L<<25; /* number should stop at null character */ {@+char temp[100]; if (gb_string(temp,'\n')!=temp+1) io_errors |= 1L<<26; /* string should be null after end of line */ gb_newline(); if (gb_string(temp,':')!=temp+5 || strcmp(temp,"Oops")) io_errors |= 1L<<27; /* string not read properly */ } if (io_errors) exit_test("Sorry, it failed. Look at the error code for clues"); if (gb_digit(10)!=-1) exit_test("Digit error not detected"); if (gb_char()!=':') io_errors |= 1L<<28; /* lost synch after |gb_string| and |gb_digit| */ if (gb_eof()) io_errors |= 1L<<29; /* premature end-of-file indication */ gb_newline(); if (!gb_eof()) io_errors |= 1L<<30; /* postmature end-of-file indication */ @* Opening a file. The call |gb_raw_open("foo")| will open file |"foo"| and initialize the checksumming process. If the file cannot be opened, |io_errors| will be set to |cant_open_file|, otherwise |io_errors| will be initialized to zero. The call |gb_open("foo")| is a stronger version of |gb_raw_open|, which is used for standard GraphBase data files like |"words.dat"| to make doubly sure that they have not been corrupted. It returns the current value of |io_errors|, which will be nonzero if any problems were detected at the beginning of the file. @= if (gb_open("test.dat")!=0) exit_test("Can't open test.dat"); @ @d gb_raw_open gb_r_open /* abbreviation for Procrustean external linkage */ @(gb_io.h@>= #define gb_raw_open gb_r_open extern void gb_raw_open(); /* open a file for GraphBase input */ extern long gb_open(); /* open a GraphBase data file; return 0 if OK */ @ @= void gb_raw_open(f) char *f; { @; @; if (cur_file) { io_errors=0; more_data=1; line_no=magic=0; tot_lines=0x7fffffff; /* allow ``infinitely many'' lines */ fill_buf(); }@+else io_errors=cant_open_file; } @ Here's a possibly system-dependent part of the code: We try first to open the data file by using the file name itself as the path name; failing that, we try to prefix the file name with the name of the standard directory for GraphBase data, if the program has been compiled with |DATA_DIRECTORY| defined. @^system dependencies@> @= cur_file=fopen(f,"r"); @^system dependencies@> #ifdef DATA_DIRECTORY if (!cur_file && (strlen(DATA_DIRECTORY)+strlen(f)= long gb_open(f) char *f; { strncpy(file_name,f,sizeof(file_name)-1); /* save the name for use by |gb_close| */ gb_raw_open(f); if (cur_file) { @; @; @; @; gb_newline(); /* the first line of real data is now in the buffer */ } return io_errors; } @ @= static char file_name[20]; /* name of the data file, without a prefix */ @ The first four lines of a typical data file should look something like this: $$\halign{\hskip5em\.{#}\hfill\cr * File "words.dat" from the Stanford GraphBase (C) 1993 Stanford University\cr * A database of English five-letter words\cr * This file may be freely copied but please do not change it in any way!\cr * (Checksum parameters 5757,526296596)\cr}$$ We actually verify only that the first four lines of a data file named |"foo"| begin respectively with the characters $$\halign{\hskip5em\.{#}\hfill\cr * File "foo"\cr *\cr *\cr * (Checksum parameters $l$,$m$)\cr}$$ where $l$ and $m$ are decimal numbers. The values of $l$ and~$m$ are stored away as |tot_lines| and |final_magic|, to be matched at the end of the file. @= sprintf(str_buf,"* File \"%s\"",f); if (strncmp(buffer,str_buf,strlen(str_buf))) return (io_errors |= bad_first_line); @ @= fill_buf(); if (*buffer!='*') return (io_errors |= bad_second_line); @ @= fill_buf(); if (*buffer!='*') return (io_errors |= bad_third_line); @ @= fill_buf(); if (strncmp(buffer,"* (Checksum parameters ",23)) return (io_errors |= bad_fourth_line); cur_pos +=23; tot_lines=gb_number(10); if (gb_char()!=',') return (io_errors |= bad_fourth_line); final_magic=gb_number(10); if (gb_char()!=')') return (io_errors |= bad_fourth_line); @* Closing a file. After all data has been input, or should have been input, we check that the file was open and that it had the correct number of lines, the correct magic number, and a correct final line. The subroutine |gb_close|, like |gb_open|, returns the value of |io_errors|, which will be nonzero if at least one problem was noticed. @= if (gb_close()!=0) exit_test("Bad checksum, or difficulty closing the file"); @ @= long gb_close() { if (!cur_file) return (io_errors |= no_file_open); fill_buf(); sprintf(str_buf,"* End of file \"%s\"",file_name); if (strncmp(buffer,str_buf,strlen(str_buf))) io_errors |= bad_last_line; more_data=buffer[0]=0; /* now the {\sc GB\_\,IO} routines are effectively shut down */ /* we have |cur_pos=buffer| */ if (fclose(cur_file)!=0) return (io_errors |= cant_close_file); cur_file=NULL; if (line_no!=tot_lines+1) return (io_errors |= wrong_number_of_lines); if (magic!=final_magic) return (io_errors |= wrong_checksum); return io_errors; } @ There is also a less paranoid routine, |gb_raw_close|, that closes user-generated files. It simply closes the current file, if any, and returns the value of the |magic| checksum. Example: The |restore_graph| subroutine in {\sc GB\_\,SAVE} uses |gb_raw_open| and |gb_raw_close| to provide system-independent input that is almost as foolproof as the reading of standard GraphBase data. @ @d gb_raw_close gb_r_close /* for Procrustean external linkage */ @(gb_io.h@>= #define gb_raw_close gb_r_close extern long gb_close(); /* close a GraphBase data file; return 0 if OK */ extern long gb_raw_close(); /* close file and return the checksum */ @ @= long gb_raw_close() { if (cur_file) { fclose(cur_file); more_data=buffer[0]=0; cur_pos=buffer; cur_file=NULL; } return magic; } @* Index. Here is a list that shows where the identifiers of this program are defined and used. gb_sort.w0000444000175000017500000001660105635233556011045 0ustar dondon% This file is part of the Stanford GraphBase (c) Stanford University 1993 @i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES! \def\title{GB\_\,SORT} @* Introduction. This short GraphBase module provides a simple utility routine called |gb_linksort|, which is used in many of the other programs. @p #include /* the \.{NULL} pointer (|NULL|) is defined here */ #include "gb_flip.h" /* we need to use the random number generator */ @h@# @@; @ @ Most of the graphs obtained from GraphBase data are parameterized, so that different effects can be obtained easily from the same underlying body of information. In many cases the desired graph is determined by selecting the ``heaviest'' vertices according to some notion of ``weight,'' and/or by taking a random sample of vertices. For example, the GraphBase routine |words(n,wt_vector,wt_threshold,seed)| creates a graph based on the |n| most common five-letter words of English, where common-ness is determined by a given weight vector. When several words have equal weight, we want to choose between them at random. In particular, this means that we can obtain a completely random choice of words if the weight vector assigns the same weight to each word. The |gb_linksort| routine is a convenient tool for this purpose. It takes a given linked list of nodes and shuffles their link fields so that the nodes can be read in decreasing order of weight, and so that equal-weight nodes appear in random order. {\sl Note: The random number generator of {\sc GB\_\,FLIP} must be initialized before |gb_linksort| is called.} The nodes sorted by |gb_linksort| can be records of any structure type, provided only that the first field is `|long| |key|' and the second field is `|struct| \\{this\_struct\_type} |*link|'. Further fields are not examined. The |node| type defined in this section is the simplest possible example of such a structure. Sorting is done by means of the |key| fields, which must each contain nonnegative integers less than $2^{31}$. After sorting is complete, the data will appear in 128 linked lists: |gb_sorted[127]|, |gb_sorted[126]|, \dots, |gb_sorted[0]|. To examine the nodes in decreasing order of weight, one can read through these lists with a routine such as $$\vcenter{\halign{#\hfil\cr |{|\cr \quad|int j;|\cr \quad|node *p;|\cr \noalign{\smallskip} \quad|for (j=127; j>=0; j--)|\cr \qquad|for (p=(node*)gb_sorted[j]; p; p=p->link)|\cr \qquad\qquad\\{look\_at}|(p)|;\cr |}|\cr}}$$ All nodes whose keys are in the range $j\cdot2^{24}\le|key|<(j+1)\cdot2^{24}$ will appear in list |gb_sorted[j]|. Therefore the results will all be found in the single list |gb_sorted[0]|, if all the keys are strictly less than~$2^{24}$. @f node int @= typedef struct node_struct { long key; /* a numeric quantity, assumed nonnegative */ struct node_struct *link; /* the next node on a list */ } node; /* applications of |gb_linksort| may have other fields after |link| */ @ In the header file, |gb_sorted| is declared to be an array of pointers to |char|, since nodes may have different types in different applications. User programs should cast |gb_sorted| to the appropriate type as in the example above. @(gb_sort.h@>= extern void gb_linksort(); /* procedure to sort a linked list */ extern char* gb_sorted[]; /* the results of |gb_linksort| */ @ Six passes of a radix sort, using radix 256, will accomplish the desired objective rather quickly. (See, for example, Algorithm 5.2.5R in {\sl Sorting and Searching}.) The first two passes use random numbers instead of looking at the key fields, thereby effectively extending the keys so that nodes with equal keys will appear in reasonably random order. We move the nodes back and forth between two arrays of lists: the external array |gb_sorted| and a private array called |alt_sorted|. @= node *gb_sorted[256]; /* external bank of lists, for even-numbered passes */ static node *alt_sorted[256]; /* internal bank of lists, for odd-numbered passes */ @ So here we go with six passes over the data. @= void gb_linksort(l) node *l; {@+register long k; /* index to destination list */ register node **pp; /* current place in list of pointers */ register node *p, *q; /* pointers for list manipulation */ @; @; @; @; @; @; } @ @= for (pp=alt_sorted+255; pp>=alt_sorted; pp--) *pp=NULL; /* empty all the destination lists */ for (p=l; p; p=q) { k=gb_next_rand() >> 23; /* extract the eight most significant bits */ q=p->link; p->link=alt_sorted[k]; alt_sorted[k]=p; } @ @= for (pp=gb_sorted+255; pp>=gb_sorted; pp--) *pp=NULL; /* empty all the destination lists */ for (pp=alt_sorted+255; pp>=alt_sorted; pp--) for (p=*pp; p; p=q) { k=gb_next_rand() >> 23; /* extract the eight most significant bits */ q=p->link; p->link=gb_sorted[k]; gb_sorted[k]=p; } @ @= for (pp=alt_sorted+255; pp>=alt_sorted; pp--) *pp=NULL; /* empty all the destination lists */ for (pp=gb_sorted+255; pp>=gb_sorted; pp--) for (p=*pp; p; p=q) { k=p->key & 0xff; /* extract the eight least significant bits */ q=p->link; p->link=alt_sorted[k]; alt_sorted[k]=p; } @ Here we must read from |alt_sorted| from 0 to 255, not from 255 to 0, to get the desired final order. (Each pass reverses the order of the lists; it's tricky, but it works.) @= for (pp=gb_sorted+255; pp>=gb_sorted; pp--) *pp=NULL; /* empty all the destination lists */ for (pp=alt_sorted; ppkey >> 8) & 0xff; /* extract the next eight bits */ q=p->link; p->link=gb_sorted[k]; gb_sorted[k]=p; } @ @= for (pp=alt_sorted+255; pp>=alt_sorted; pp--) *pp=NULL; /* empty all the destination lists */ for (pp=gb_sorted+255; pp>=gb_sorted; pp--) for (p=*pp; p; p=q) { k=(p->key >> 16) & 0xff; /* extract the next eight bits */ q=p->link; p->link=alt_sorted[k]; alt_sorted[k]=p; } @ The most significant bits will lie between 0 and 127, because we assumed that the keys are nonnegative and less than $2^{31}$. (A similar routine would be able to sort signed integers, or unsigned long integers, but the \CEE/ code would not then be portable.) @= for (pp=gb_sorted+255; pp>=gb_sorted; pp--) *pp=NULL; /* empty all the destination lists */ for (pp=alt_sorted; ppkey >> 24) & 0xff; /* extract the most significant bits */ q=p->link; p->link=gb_sorted[k]; gb_sorted[k]=p; } @* Index. Here is a list that shows where the identifiers of this program are defined and used. gb_basic.w0000444000175000017500000030353111240102546011117 0ustar dondon% 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}. @= 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@# @@; @@; @@; @ Several of the programs below allocate arrays that will be freed again before the routine is finished. @= 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 @= 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= #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)@] @ @= 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? */ {@+@@;@+@q{@>@t}\6{@>@q}@> long n; /* total number of vertices */ long p; /* $\vert|piece|\vert$ */ long l; /* length of current arc */ @; @; @; 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. @= 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 @= 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 */ @ @= 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;@+} @; 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. @= 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 @= {@+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"); @; } @ 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. @= {@+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. @= @; p=piece; if (p<0) p=-p; while (1) { @; while (1) { @; @; } } @ 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@> @= {@+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. @= 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]= 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. @= for (k=1;k<=d;k++) xx[k]=0; for (v=new_graph->vertices;;v++) { @; 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|. @= for (k=1;k<=d;k++) yy[k]=xx[k]+del[k]; for (l=1;;l++) { @; if (piece<0) @; @; if (piece>0) goto no_more; for (k=1;k<=d;k++) yy[k]+=del[k]; } no_more:@; @ @= { for (k=1;k<=d;k++) if (yy[k]!=xx[k]) goto unequal; goto no_more; unequal:; } @ @= 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]); } } @ @= 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. @ @= 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? */ {@+@@;@# @; @; @; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* darn, we ran out of memory somewhere back there */ } return new_graph; } @ @= 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]; @; done: /* now |nn[0]| through |nn[d]| are set up */ @ @= @; 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}).$$ @= {@+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++) @; 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. @= { 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. @= 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) { @; @; hash_in(v); /* enter |v->name| into the hash table (via utility fields |u,v|) */ @; v++; @; } } last:@+if (v!=new_graph->vertices+new_graph->n) panic(impossible); /* can't happen */ @ @= 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. @= for (k=d-1;;k--) { if (xx[k]= {@+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. @= for (j=0;j 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)@] @ @= 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? */ {@+@@;@# @; @; @; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* rats, we ran out of memory somewhere back there */ } return new_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|. @= 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) { @; @; @; v++; @; } } 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| */ @= {@+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]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 @^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. @= 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? */ {@+@@;@+@q{@>@t}\6{@>@q}@> register long n; /* total number of elements in multiset */ @; @; @; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* shucks, we ran out of memory somewhere back there */ } return new_graph; } @ @= 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 */ @; @; @ 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. @= {@+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}\,.$$ @= {@+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++) @; 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. @= 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= {@+register long *xtab,*ytab,*ztab; /* permutations and their inversions */ long m=0; /* current number of inversions */ @; v=new_graph->vertices; while (1) { @; @; v++; @; } last:@+if (v!=new_graph->vertices+new_graph->n) panic(impossible); /* can't happen */ gb_free(working_storage); } @ @= 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); ztab=ytab+(n+1); for (j=0,k=1,s=nn[0];;k++) { xtab[k]=ztab[k]=j; /* |ytab[k]=0| */ if (k==s) { if (++j>d) break; else s+=nn[j]; } } @ Here is the heart of the permutation logic. We find the largest~$k$ such that $y_k$ can legitimately be increased by~1. When we encounter a~$k$ for which $y_k$ cannot be increased, we set $y_k=0$ and adjust the $x$'s accordingly. If no $y_k$ can be increased, we are done. @= for (k=n;k;k--) { if (mztab[k-1]) goto move; if (ytab[k]) { for (j=k-ytab[k];j= static char *short_imap="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz_^~&@@,;.:?!%#$+-*/|<=>()[]{}`'"; @ @= {@+register char *p; register long *q; for (p=&buffer[n-1],q=&xtab[n];q>xtab;p--,q--) *p=short_imap[*q]; v->name=gb_save_string(buffer); hash_in(v); /* enter |v->name| into the hash table (via utility fields |u,v|) */ } @ Since we are generating the vertices in lexicographic order of their inversions, it is easy to identify all adjacent vertices that precede the current setting of $(x_1,\ldots,x_n)$. We locate them via their symbolic names. @= for (j=1;jxtab[j+1]) {@+register Vertex *u; /* previous vertex adjacent to |v| */ buffer[j-1]=short_imap[xtab[j+1]];@+buffer[j]=short_imap[xtab[j]]; u=hash_out(buffer); if (u==NULL) panic(impossible+2); /* can't happen */ if (directed) gb_new_arc(u,v,1L); else gb_new_edge(u,v,1L); buffer[j-1]=short_imap[xtab[j]];@+buffer[j]=short_imap[xtab[j+1]]; } @* Partition graphs. The subroutine call |parts(n,max_parts,max_size,directed)| creates a graph whose vertices represent the different ways to partition the integer~|n| into at most |max_parts| parts, where each part is at most |max_size|. Two partitions are adjacent in the graph if one can be obtained from the other by combining two parts. Each arc has length~1. For example, the partitions of~5 are $$5,\quad 4+1,\quad 3+2,\quad 3+1+1,\quad 2+2+1, \quad 2+1+1+1,\quad 1+1+1+1+1.$$ Here 5 is adjacent to $4+1$ and to $3+2$; $4+1$ is adjacent also to $3+1+1$ and to $2+2+1$; $3+2$ is adjacent also to $3+1+1$ and to $2+2+1$; etc. If |max_size| is 3, the partitions 5 and $4+1$ would not be included in the graph. If |max_parts| is 3, the partitions $2+1+1+1$ and $1+1+1+1+1$ would not be included. If |max_parts| or |max_size| are zero, they are reset to be equal to~|n| so that they make no restriction on the partitions. If |directed| is nonzero, the graph will contain only directed arcs from partitions to their neighbors that have exactly one more part. The special case when we want to generate all $p(n)$ partitions of the integer~$n$ can be obtained by calling |all_parts(n,directed)|. @(gb_basic.h@>= #define all_parts(n,directed) @[parts((long)(n),0L,0L,(long)(directed))@] @ The program for |parts| is very similar in structure to the program for |perms| already considered. @= Graph *parts(n,max_parts,max_size,directed) unsigned long n; /* the number being partitioned */ unsigned long max_parts; /* maximum number of parts */ unsigned long max_size; /* maximum size of each part */ long directed; /* should the graph be directed? */ {@+@@;@# if (max_parts==0 || max_parts>n) max_parts=n; if (max_size==0 || max_size>n) max_size=n; if (max_parts>MAX_D) panic(bad_specs); /* too many parts allowed */ @; @; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* doggone it, we ran out of memory somewhere back there */ } return new_graph; } @ The number of vertices is the coefficient of $z^n$ in the $z$-binomial coefficient ${m+p\choose m}_z$, where $m=|max_parts|$ and $p=|max_size|$. This coefficient is calculated as in the |perms| routine. @= {@+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 */ coef[0]=1; for (k=1;k<=max_parts;k++) { for (j=n,i=n-k-max_size;i>=0;i--,j--) coef[j]-=coef[i]; for (j=k,i=0;j<=n;i++,j++) { coef[j]+=coef[i]; if (coef[j]>1000000000) panic(very_bad_specs); /* way too big */ } } 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 */ sprintf(new_graph->id,"parts(%lu,%lu,%lu,%d)", n,max_parts,max_size,directed?1:0); strcpy(new_graph->util_types,"VVZZZZZZZZZZZZ"); /* hash table will be used */ } @ As we generate the partitions, we maintain the numbers $\sigma_j=n-(x_1+\cdots+x_{j-1})=x_j+x_{j+1}+\cdots\,$, somewhat as we did in the |simplex| routine. We set $x_0=|max_size|$ and $y_j=|max_parts|+1-j$; then when values $(x_1,\ldots,x_{j-1})$ are given, the conditions $$\sigma_j/y_j\le x_j\le \sigma_j,\qquad x_j\le x_{j-1}$$ characterize the legal values of~$x_j$. @= v=new_graph->vertices; xx[0]=max_size;@+sig[1]=n; for (k=max_parts,s=1;k>0;k--,s++) yy[k]=s; if (max_size*max_parts>=n) { k=1;@+xx[1]=(n-1)/max_parts+1; /* $\lceil n/|max_parts|\rceil$ */ while (1) { @; @; @; v++; @; } } last:@+if (v!=new_graph->vertices+new_graph->n) panic(impossible); /* can't happen */ @ @= for (s=sig[k]-xx[k],k++;s;k++) { sig[k]=s; xx[k]=(s-1)/yy[k]+1; s-=xx[k]; } d=k-1; /* the smallest part is $x_d$ */ @ Here we seek the largest $k$ such that $x_k$ can be increased without violating the necessary and sufficient conditions stated earlier. @= if (d==1) goto last; for (k=d-1;;k--) { if (xx[k]= {@+register char *p=buffer; /* string pointer */ for (k=1;k<=d;k++) { sprintf(p,"+%ld",xx[k]); while (*p) p++; } v->name=gb_save_string(&buffer[1]); /* omit |buffer[0]|, which is |'+'| */ hash_in(v); /* enter |v->name| into the hash table (via utility fields |u,v|) */ } @ Since we are generating the partitions in lexicographic order of their parts, it is reasonably easy to identify all adjacent vertices that precede the current setting of $(x_1,\ldots,x_d)$, by splitting $x_j$ into two parts when $x_j\ne x_{j+1}$. We locate previous partitions via their symbolic names. @= if (d; } nn[j]=xx[j]; } } @ The values of $(x_1,\ldots,x_{j-1})$ have already been copied into $(n_1,\ldots,n_{j-1})$. Our job is to copy the smaller parts $(x_{j+1},\ldots,x_d)$ while inserting $a$ and $b$ in their proper places, knowing that $a\ge b$. @= {@+register Vertex *u; /* previous vertex adjacent to |v| */ register char *p=buffer; for (k=j+1;xx[k]>a;k++) nn[k-1]=xx[k]; nn[k-1]=a; for (;xx[k]>b;k++) nn[k]=xx[k]; nn[k]=b; for (;k<=d;k++) nn[k+1]=xx[k]; for (k=1;k<=d+1;k++) { sprintf(p,"+%ld",nn[k]); while (*p) p++; } u=hash_out(&buffer[1]); if (u==NULL) panic(impossible+2); /* can't happen */ if (directed) gb_new_arc(v,u,1L); else gb_new_edge(v,u,1L); } @* Binary tree graphs. The subroutine call |binary(n,max_height,directed)| creates a graph whose vertices represent the binary trees with $n$ internal nodes and with all leaves at a distance that is at most |max_height| from the root. Two binary trees are adjacent in the graph if one can be obtained from the other by a single application of the associative law for binary operations, i.e., by replacing some subtree of the form $(\alpha\cdot\beta)\cdot\gamma$ by the subtree $\alpha\cdot (\beta\cdot\gamma)$. (This transformation on binary trees is often called a ``rotation.'') If the |directed| parameter is nonzero, the directed arcs go from a tree containing $(\alpha\cdot\beta)\cdot\gamma$ to a tree containing $\alpha\cdot(\beta\cdot\gamma)$ in its place; otherwise the graph is undirected. Each arc has length~1. For example, the binary trees with three internal nodes form a circuit of length~5. They are $$\mathcode`.="2201 % \cdot (a.b).(c.d),\quad a.(b.(c.d)),\quad a.((b.c).d),\quad (a.(b.c)).d,\quad ((a.b).c).d,$$ if we use infix notation and name the leaves $(a,b,c,d)$ from left to right. Here each tree is related to its two neighbors by associativity. The first and last trees are also related in the same way. If |max_height=0|, it is changed to |n|, which means there is no restriction on the height of a leaf. In this case the graph will have exactly ${2n+1\choose n}/ (2n+1)$ vertices; furthermore, each vertex will have exactly $n-1$ neighbors, because a rotation will be possible just above every internal node except the root. The graph in this case can also be interpreted geometrically: The vertices are in one-to-one correspondence with the triangulations of a regular $(n+2)$-gon; two triangulations are adjacent if and only if one is obtained from the other by replacing the pair of adjacent triangles $ABC,DCB$ by the pair $ADC,BDA$. The partial ordering corresponding to the directed graph on ${2n+1\choose n}/(2n+1)$ vertices created by |all_trees(n,1)| is a lattice with interesting properties. See Huang and Tamari, @^Huang, Samuel Shung@> @^Tamari, Dov@> {\sl Journal of Combinatorial Theory\/ \bf A13} (1972), 7--13. @(gb_basic.h@>= #define all_trees(n,directed) @[binary((long)(n),0L,(long)(directed))@] @ The program for |binary| is very similar in structure to the program for |parts| already considered. But the details are more exciting. @= Graph *binary(n,max_height,directed) unsigned long n; /* the number of internal nodes */ unsigned long max_height; /* maximum height of a leaf */ long directed; /* should the graph be directed? */ {@+@@;@# if (2*n+2>BUF_SIZE) panic(bad_specs); /* |n| is too huge for us */ if (max_height==0 || max_height>n) max_height=n; if (max_height>30) panic(very_bad_specs); /* more than a billion vertices */ @; @; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* uff da, we ran out of memory somewhere back there */ } return new_graph; } @ The number of vertices is the coefficient of $z^n$ in the power series $G_h$, where $h=|max_height|$ and $G_h$ satisfies the recurrence $$G_0=1,\qquad G_{h+1}=1+z G_h^2.$$ The coefficients of $G_5$ are $\le55308$, but the coefficients of $G_6$ are much larger; they exceed one billion when $28\le n\le49$, and they exceed one million when $17\le n\le 56$. In order to avoid overflow during this calculation, we use a special method when $h\ge6$ and $n\ge20$: In such cases, graphs of reasonable size arise only if $n\ge 2^h-7$, and we look at the coefficient of $z^{-(2^h-1-n)}$ in $R_h=G_h/z^{2^h-1}$, which is a power series in $z^{-1}$ defined by the recurrence $$R_0=1,\qquad R_{h+1}=R_h^2+z^{1-2^{h+1}}.$$ @= {@+long nverts; /* the number of vertices */ if (n>=20 && max_height>=6) @@; else { nn[0]=nn[1]=1; for (k=2;k<=n;k++) nn[k]=0; for (j=2;j<=max_height;j++) for (k=n-1;k;k--) { for (s=0,i=k;i>=0;i--) s+=nn[i]*nn[k-i]; /* overflow impossible */ nn[k+1]=s; } nverts=nn[n]; } 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,"binary(%lu,%lu,%d)", n,max_height,directed?1:0); strcpy(new_graph->util_types,"VVZZZZZZZZZZZZ"); /* hash table will be used */ } @ The smallest nontrivial graph that is unilaterally disallowed by this procedure on the grounds of size limitations occurs when |max_height=6| and |n=20|; it has 14,162,220 vertices. @= {@+register float ss; d=(1L<8) panic(bad_specs+1); /* too many vertices */ if (d<0) nverts=0; else { nn[0]=nn[1]=1; for (k=2;k<=d;k++) nn[k]=0; for (j=2;j<=max_height;j++) { for (k=d;k;k--) { for (ss=0.0,i=k;i>=0;i--) ss+=((float)nn[i])*((float)nn[k-i]); if (ss>MAX_NNN) panic(very_bad_specs+1); /* way too big */ for (s=0,i=k;i>=0;i--) s+=nn[i]*nn[k-i]; /* overflow impossible */ nn[k]=s; } i=(1L<y_j$, $x_j$ is forced to be~1. If $l_j=1$, $x_j$ is forced to be~0. If the number of 1-bits of $y_j$ is equal to $\sigma_j$, $x_j$ is forced to be~0. Otherwise $x_j$ can be either 0 or~1, and it will be possible to complete the partial solution $x_0\ldots x_j$ to a full Polish prefix code $x_0\ldots x_{2n}$. For example, here are the arrays for one of the binary trees that is generated when $n=h=3$: $$\vcenter{\halign{$\hfil#$\quad=&&\quad#\cr j &0&1&2&3&4&5&6\cr l_j &8&4&2&2&1&1&4\cr y_j &0&4&6&4&5&4&0\cr \sigma_j&3&3&3&2&2&1&0\cr x_j &1&1&0&1&0&0&0\cr}}$$ If $x_j=1$ and $j<2n$, we have $l_{j+1}=l_j/2$, $y_{j+1}=y_j+l_{j+1}$, and $\sigma_{j+1}=\sigma_j$. If $x_j=0$ and $j<2n$, we have $l_{j+1}= 2^t$, $y_{j+1}=y_j-2^t$, and $\sigma_{j+1}=\sigma_j-1$, where $2^t$ is the least power of~2 in the binary representation of~$y_j$. It is not difficult to prove by induction that $\sigma_j= {@+register long *xtab,*ytab,*ltab,*stab; @; v=new_graph->vertices; if (ltab[0]>n) { k=0;@+xtab[0]=n?1:0; while (1) { @; @; @; v++; @; } } } last:@+if (v!=new_graph->vertices+new_graph->n) panic(impossible); /* can't happen */ gb_free(working_storage); @ @= xtab=gb_typed_alloc(8*n+4,long,working_storage); if (gb_trouble_code) { /* no room for |xtab| */ gb_recycle(new_graph);@+panic(no_room+2);@+} d=n+n; ytab=xtab+(d+1); ltab=ytab+(d+1); stab=ltab+(d+1); ltab[0]=1L<= for (j=k+1;j<=d;j++) { if (xtab[j-1]) { ltab[j]=ltab[j-1]>>1; ytab[j]=ytab[j-1]+ltab[j]; stab[j]=stab[j-1]; }@+else { ytab[j]=ytab[j-1]&(ytab[j-1]-1); /* remove least significant 1-bit */ ltab[j]=ytab[j-1]-ytab[j]; stab[j]=stab[j-1]-1; } if (stab[j]<=ytab[j]) xtab[j]=0; else xtab[j]=1; /* this is the lexicographically smallest completion */ } @ As in previous routines, we seek the largest $k$ such that $x_k$ can be increased without violating the necessary and sufficient conditions stated earlier. @= for (k=d-1;;k--) { if (k<=0) goto last; /* this happens only when |n<=1| */ if (xtab[k]) break; /* find rightmost 1 */ } for (k--;;k--) { if (xtab[k]==0 && ltab[k]>1) break; if (k==0) goto last; } xtab[k]++; @ In the |name| field, we encode internal nodes of the binary tree by `\..' and leaves by `\.x'. Thus the five trees shown above in binary code will be named $$\.{.x.x.xx},\quad \.{.x..xxx},\quad \.{..xx.xx},\quad \.{..x.xxx},\quad \.{...xxxx},$$ respectively. @= {@+register char *p=buffer; /* string pointer */ for (k=0;k<=d;k++,p++) *p=(xtab[k]? '.': 'x'); v->name=gb_save_string(buffer); hash_in(v); /* enter |v->name| into the hash table (via utility fields |u,v|) */ } @ Since we are generating the trees in lexicographic order of their Polish prefix notation, it is relatively easy to find all pairs of trees that are adjacent via one application of the associative law: We simply replace a substring of the form $\..\..\alpha\beta$ by $\..\alpha\..\beta$, when $\alpha$ and $\beta$ are Polish prefix strings. The result comes earlier in lexicographic order, so it will be an existing vertex unless it violates the |max_height| restriction. @= for (j=0;j=0;s+=(xtab[i+1]<<1)-1,i++) xtab[i]=xtab[i+1]; xtab[i]=1; {@+register char *p=buffer; /* string pointer */ register Vertex *u; for (k=0;k<=d;k++,p++) *p=(xtab[k]? '.': 'x'); u=hash_out(buffer); if (u) { if (directed) gb_new_arc(v,u,1L); else gb_new_edge(v,u,1L); } } for (i--;i>j;i--) xtab[i+1]=xtab[i]; /* restore |xtab| */ xtab[i+1]=1; } @* Complementing and copying. We have seen how to create a wide variety of basic graphs with the |board|, |simplex|, |subsets|, |perms|, |parts|, and |binary| procedures. The remaining routines of {\sc GB\_\,BASIC} are somewhat different. They transform existing graphs into new ones, thereby presenting us with an almost mind-boggling array of further possibilities. The first of these transformations is perhaps the simplest. It complements a given graph, making vertices adjacent if and only if they were previously nonadjacent. More precisely, the subroutine call |complement(g,copy,self,directed)| returns a graph with the same vertices as |g|, but with complemented arcs. If |self!=0|, the new graph will have a self-loop from a vertex |v| to itself when the original graph did not; if |self=0|, the new graph will have no self-loops. If |directed!=0|, the new graph will have an arc from |u| to |v| when the original graph did not; if |directed=0|, the new graph will be undirected, and it will have an edge between |u| and~|v| when the original graph did not. In the latter case, the original graph should also be undirected (that is, its arcs should come in pairs, as described in the |gb_new_edge| routine of {\sc GB\_\,GRAPH}). If |copy!=0|, a double complement will actually be done. This means that the new graph will essentially be a copy of the old, except that duplicate arcs (and possibly self-loops) will be removed. Regardless of the value of |copy|, information that might have been present in the utility fields will not be copied, and arc lengths will all be set to~1. One possibly useful feature of the graphs returned by |complement| is worth noting. The vertices adjacent to~|v|, namely the list $$\hbox{|v->arcs->tip|,\quad |v->arcs->next->tip|,\quad |v->arcs->next->next->tip|,\quad \dots\thinspace,}$$ will be in strictly decreasing order (except in the case of an undirected self-loop, when |v| itself will appear twice in succession). @ @= Graph *complement(g,copy,self,directed) Graph *g; /* graph to be complemented */ long copy; /* should we double-complement? */ long self; /* should we produce self-loops? */ long directed; /* should the graph be directed? */ {@+@@;@+@q{@>@t}\6{@>@q}@> register long n; register Vertex *u; register siz_t delta; /* difference in memory addresses */ if (g==NULL) panic(missing_operand); /* where's |g|? */ @; sprintf(buffer,",%d,%d,%d)",copy?1:0,self?1:0,directed?1:0); make_compound_id(new_graph,"complement(",g,buffer); @; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* worse luck, we ran out of memory somewhere back there */ } return new_graph; } @ In several of the following routines, it is efficient to circumvent \CEE/'s normal rules for pointer arithmetic, and to use the fact that the vertices of a graph being copied are a constant distance away in memory from the vertices of its clone. @d vert_offset(v,delta) ((Vertex*)(((siz_t)v)+delta)) @^pointer hacks@> @= n=g->n; new_graph=gb_new_graph(n); if (new_graph==NULL) panic(no_room); /* out of memory before we're even started */ delta=((siz_t)(new_graph->vertices))-((siz_t)(g->vertices)); for (u=new_graph->vertices,v=g->vertices;vvertices+n;u++,v++) u->name=gb_save_string(v->name); @ A temporary utility field in the new graph is used to remember which vertices are adjacent to a given vertex in the old one. We stamp the |tmp| field of~|v| with a pointer to~|u| when there's an arc from |u| to~|v|. @d tmp u.V /* utility field |u| for temporary use as a vertex pointer */ @= for (v=g->vertices;vvertices+n;v++) {@+register Vertex *vv; u=vert_offset(v,delta); /* vertex in |new_graph| corresponding to |v| in |g| */ {@+register Arc *a; for (a=v->arcs;a;a=a->next) vert_offset(a->tip,delta)->tmp=u; } if (directed) { for (vv=new_graph->vertices;vvvertices+n;vv++) if ((vv->tmp==u && copy) || (vv->tmp!=u && !copy)) if (vv!=u || self) gb_new_arc(u,vv,1L); }@+else { for (vv=(self?u:u+1);vvvertices+n;vv++) if ((vv->tmp==u && copy) || (vv->tmp!=u && !copy)) gb_new_edge(u,vv,1L); } } for (v=new_graph->vertices;vvertices+n;v++) v->tmp=NULL; @* Graph union and intersection. Another simple way to get new graphs from old ones is to take the union or intersection of their sets of arcs. The subroutine call |gunion(g,gg,multi,directed)| produces a graph with the vertices and arcs of |g| together with the arcs of another graph~|gg|. The subroutine call |intersection(g,gg,multi, directed)| produces a graph with the vertices of |g| but with only the arcs that appear in both |g| and |gg|. In both cases we assume that |gg| has the same vertices as |g|, in the sense that vertices in the same relative position from the beginning of the vertex array are considered identical. If the actual number of vertices in |gg| exceeds the number in |g|, the extra vertices and all arcs touching them in~|gg| are suppressed. The input graphs are assumed to be undirected, unless the |directed| parameter is nonzero. Peculiar results might occur if you mix directed and undirected graphs, but the subroutines will not ``crash'' when they are asked to produce undirected output from directed input. If |multi| is nonzero, the new graph may have multiple edges: Suppose there are $k_1$ arcs from $u$ to $v$ in |g| and $k_2$ such arcs in |gg|. Then there will be $k_1+k_2$ in the union and $\min(k_1,k_2)$ in the intersection when |multi!=0|, but at most one in the union or intersection when |multi=0|. The lengths of arcs are copied to the union graph when |multi!=0|; the minimum length of multiple arcs is retained in the union when |multi=0|. The lengths of arcs in the intersection graph are a bit trickier. If multiple arcs occur in |g|, their minimum length, |l|, is computed. Then we compute the maximum of |l| and the lengths of corresponding arcs in |gg|. If |multi=0|, only the minimum of those maxima will survive. @ @= Graph *gunion(g,gg,multi,directed) Graph *g,*gg; /* graphs to be united */ long multi; /* should we reproduce multiple arcs? */ long directed; /* should the graph be directed? */ {@+@@;@+@q{@>@t}\6{@>@q}@> register long n; register Vertex *u; register siz_t delta,ddelta; /* differences in memory addresses */ if (g==NULL || gg==NULL) panic(missing_operand); /* where are |g| and |gg|? */ @; sprintf(buffer,",%d,%d)",multi?1:0,directed?1:0); make_double_compound_id(new_graph,"gunion(",g,",",gg,buffer); ddelta=((siz_t)(new_graph->vertices))- ((siz_t)(gg->vertices)); @; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* uh oh, we ran out of memory somewhere back there */ } return new_graph; } @ @= for (v=g->vertices;vvertices+n;v++) { register Arc *a; register Vertex *vv=vert_offset(v,delta); /* vertex in |new_graph| corresponding to |v| in |g| */ register Vertex *vvv=vert_offset(vv,-ddelta); /* vertex in |gg| corresponding to |v| in |g| */ for (a=v->arcs;a;a=a->next) { u=vert_offset(a->tip,delta); @; } if (vvvvertices+gg->n) for (a=vvv->arcs;a;a=a->next) { u=vert_offset(a->tip,ddelta); if (uvertices+n) @; } } for (v=new_graph->vertices;vvertices+n;v++) v->tmp=NULL,v->tlen=NULL; @ We use the |tmp| trick of |complement| to remember which arcs have already been recorded from |u|, and we extend it so that we can maintain minimum lengths. Namely, |uu->tmp| will equal |u| if and only if we have already seen an arc from |u| to |uu|; and if so, |uu->tlen| will be one such arc. In the undirected case, |uu->tlen| will point to the first arc of an edge pair that touches~|u|. The only thing slightly nontrivial here is the way we keep undirected edges grouped in pairs. We generate a new edge from |vv| to |u| only if |vv<=u|, and if equality holds we advance~|a| so that we don't see the self-loop in both directions. Similar logic will be repeated in many of the programs below. @d tlen z.A /* utility field |z| regarded as a pointer to an arc */ @= {@+register Arc *b; if (directed) { if (multi || u->tmp!=vv) gb_new_arc(vv,u,a->len); else { b=u->tlen; if (a->lenlen) b->len=a->len; } u->tmp=vv; /* remember that we've seen this */ u->tlen=vv->arcs; }@+else if (u>=vv) { if (multi || u->tmp!=vv) gb_new_edge(vv,u,a->len); else { b=u->tlen; if (a->lenlen) b->len=(b+1)->len=a->len; } u->tmp=vv; u->tlen=vv->arcs; if (u==vv && a->next==a+1) a++; /* bypass second half of self-loop */ } } @ @= Graph *intersection(g,gg,multi,directed) Graph *g,*gg; /* graphs to be intersected */ long multi; /* should we reproduce multiple arcs? */ long directed; /* should the graph be directed? */ {@+@@;@+@q{@>@t}\6{@>@q}@> register long n; register Vertex *u; register siz_t delta,ddelta; /* differences in memory addresses */ if (g==NULL || gg==NULL) panic(missing_operand); /* where are |g| and |gg|? */ @; sprintf(buffer,",%d,%d)",multi?1:0,directed?1:0); make_double_compound_id(new_graph,"intersection(",g,",",gg,buffer); ddelta=((siz_t)(new_graph->vertices))- ((siz_t)(gg->vertices)); @; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* whoops, we ran out of memory somewhere back there */ } return new_graph; } @ Two more temporary utility fields are needed here. @d mult v.I /* utility field |v|, counts multiplicity of arcs */ @d minlen w.I /* utility field |w|, records the smallest length */ @= for (v=g->vertices;vvertices+n;v++) {@+register Arc *a; register Vertex *vv=vert_offset(v,delta); /* vertex in |new_graph| corresponding to |v| in |g| */ register Vertex *vvv=vert_offset(vv,-ddelta); /* vertex in |gg| corresponding to |v| in |g| */ if (vvv>=gg->vertices+gg->n) continue; @; for (a=vvv->arcs;a;a=a->next) { u=vert_offset(a->tip,ddelta); if (u>=new_graph->vertices+n) continue; if (u->tmp==vv) {@+long l=u->minlen; if (a->len>l) l=a->len; /* maximum */ if (u->mult<0) @@; else @; } } } @; @ @= { if (directed) gb_new_arc(vv,u,l); else { if (vv<=u) gb_new_edge(vv,u,l); if (vv==u && a->next==a+1) a++; /* skip second half of self-loop */ } if (!multi) { u->tlen=vv->arcs; u->mult=-1; }@+else if (u->mult==0) u->tmp=NULL; else u->mult--; } @ We get here if and only if |multi=0| and |gg|~has more than one arc from |vv| to~|u| and |g|~has at least one arc from |vv| to~|u|. @= {@+register Arc *b=u->tlen; /* previous arc or edge from |vv| to |u| */ if (llen) { b->len=l; if (!directed) (b+1)->len=l; } } @ @= for (a=v->arcs;a;a=a->next) { u=vert_offset(a->tip,delta); if (u->tmp==vv) { u->mult++; if (a->lenminlen) u->minlen=a->len; }@+else u->tmp=vv, u->mult=0, u->minlen=a->len; if (u==vv && !directed && a->next==a+1) a++; /* skip second half of self-loop */ } @ @= for (v=new_graph->vertices;vvertices+n;v++) { v->tmp=NULL; v->tlen=NULL; v->mult=0; v->minlen=0; } @* Line graphs. The next operation in {\sc GB\_\,BASIC}'s repertoire constructs the so-called line graph of a given graph~$g$. The subroutine that does this is invoked by calling `|lines(g,directed)|'. If |directed=0|, the line graph has one vertex for each edge of~|g|; two vertices are adjacent if and only if the corresponding edges have a common vertex. If |directed!=0|, the line graph has one vertex for each arc of~|g|; there is an arc from vertex |u| to vertex |v| if and only if the arc corresponding to~|u| ends at the vertex that begins the arc corresponding to~|v|. All arcs of the line graph will have length~1. Utility fields |u.V| and |v.V| of each vertex in the line graph will point to the vertices of |g| that define the corresponding arc or edge, and |w.A| will point to the arc from |u.V| to |v.V| in~|g|. In the undirected case we will have |u.V<=v.V|. @= Graph *lines(g,directed) Graph *g; /* graph whose lines will become vertices */ long directed; /* should the graph be directed? */ {@+@@;@+@q{@>@t}\6{@>@q}@> register long m; /* the number of lines */ register Vertex *u; if (g==NULL) panic(missing_operand); /* where is |g|? */ @; if (directed) @@; else @; @; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* (sigh) we ran out of memory somewhere back there */ } return new_graph; near_panic:@; } @ We want to add a data structure to |g| so that the line graph can be built efficiently. But we also want to preserve |g| so that it exhibits no traces of occupation when |lines| has finished its work. To do this, we will move utility field~|v->z| temporarily into a utility field~|u->z| of the line graph, where |u| is the first vertex having |u->u.V==v|, whenever such a |u| exists. Then we'll set |v->map=u|. We will then be able to find |u| when |v| is given, and we'll be able to cover our tracks later. In the undirected case, further structure is needed. We will temporarily change the |tip| field in the second arc of each edge pair so that it points to the line-graph vertex that points to the first arc of the pair. The |util_types| field of the graph does not indicate the fact that utility fields |u.V|, |v.V|, and |w.A| of each vertex will be set, because those utility fields are pointers from the new graph to the original graph. The |save_graph| procedure does not deal with pointers between graphs. @d map z.V /* the |z| field treated as a vertex pointer */ @= for (u=new_graph->vertices,v=NULL;uvertices+m;u++) { if (u->u.V!=v) { v=u->u.V; /* original vertex of |g| */ v->map=u->map; /* restore original value of |v->z| */ u->map=NULL; } if (!directed) ((u->w.A)+1)->tip=v; } @ Special care must be taken to avoid chaos when the user is trying to construct the undirected line graph of a directed graph. Otherwise we might trash the memory, or we might leave the original graph in a garbled state with pointers leading into supposedly free space. @= m=(directed? g->m: (g->m)/2); new_graph=gb_new_graph(m); if (new_graph==NULL) panic(no_room); /* out of memory before we're even started */ make_compound_id(new_graph,"lines(",g,directed? ",1)": ",0)"); u=new_graph->vertices; for (v=g->vertices+g->n-1;v>=g->vertices;v--) {@+register Arc *a; register long mapped=0; /* has |v->map| been set? */ for (a=v->arcs;a;a=a->next) {@+register Vertex *vv=a->tip; if (!directed) { if (vv=g->vertices+g->n) goto near_panic; /* original graph not undirected */ } @; if (!mapped) { u->map=v->map; /* |z.V=map| incorporates all bits of utility field |z|, whatever its type */ v->map=u; mapped=1; } u++; } } if (u!=new_graph->vertices+m) goto near_panic; @ @= m=u-new_graph->vertices; @; gb_recycle(new_graph); panic(invalid_operand); /* |g| did not obey the conventions for an undirected graph */ @ The vertex names in the line graph are pairs of original vertex names, separated by `\.{--}' when undirected, `\.{->}' when directed. If either of the original names is horrendously long, the villainous Procrustes chops it off arbitrarily so that it fills at most half of the name buffer. @= u->u.V=v; u->v.V=vv; u->w.A=a; if (!directed) { if (u>=new_graph->vertices+m || (a+1)->tip!=v) goto near_panic; if (v==vv && a->next==a+1) a++; /* skip second half of self-loop */ else (a+1)->tip=u; } sprintf(buffer,"%.*s-%c%.*s",(BUF_SIZE-3)/2,v->name,@| directed? '>': '-',BUF_SIZE/2-1,vv->name); u->name=gb_save_string(buffer); @ @= for (u=new_graph->vertices;uvertices+m;u++) { v=u->v.V; if (v->arcs) { /* |v->map| has been set up */ v=v->map; do@+{gb_new_arc(u,v,1L); v++; }@+while (v->u.V==u->v.V); } } @ An undirected line graph will contain no self-loops. It contains multiple edges only if the original graph did; in that case, there are two edges joining a line to each of its parallel mates, because each mate hits both of its endpoints. The details of this section deserve careful study. We use the fact that the first vertices of the lines occur in nonincreasing order. @= for (u=new_graph->vertices;uvertices+m;u++) {@+register Vertex *vv; register Arc *a;@+register long mapped=0; v=u->u.V; /* we look first for prior lines that touch the first vertex */ for (vv=v->map;vvv.V; /* then we look for prior lines that touch the other one */ for (a=v->arcs;a;a=a->next) { vv=a->tip; if (vv=new_graph->vertices) gb_new_edge(u,vv,1L); else if (vv>=v && vvvertices+g->n) mapped=1; } if (mapped && v>u->u.V) for (vv=v->map;vv->u.V==v;vv++) gb_new_edge(u,vv,1L); } @* Graph products. Three ways have traditionally been used to define the product of two graphs. In all three cases the vertices of the product graph are ordered pairs $(v,v')$, where $v$ and $v'$ are vertices of the original graphs; the difference occurs in the definition of arcs. Suppose $g$ has $m$ arcs and $n$ vertices, while $g'$ has $m'$ arcs and $n'$ vertices. The {\sl cartesian product\/} of $g$ and~$g'$ has $mn'+m'n$ arcs, namely from $(u,u')$ to $(v,u')$ whenever there's an arc from $u$ to $v$ in~$g$, and from $(u,u')$ to $(u,v')$ whenever there's an arc from $u'$ to $v'$ in~$g'$. The {\sl direct product\/} has $mm'$ arcs, namely from $(u,u')$ to $(v,v')$ in the same circumstances. The {\sl strong product\/} has both the arcs of the cartesian product and the direct product. Notice that an undirected graph with $m$ edges has $2m$ arcs. Thus the number of edges in the direct product of two undirected graphs is twice the product of the number of edges in the individual graphs. A self-loop in~$g$ will combine with an edge in~$g'$ to make two parallel edges in the direct product. The subroutine call |product(g,gg,type,directed)| produces the product graph of one of these three types, where |type=0| for cartesian product, |type=1| for direct product, and |type=2| for strong product. The length of an arc in the cartesian product is copied from the length of the original arc that it replicates; the length of an arc in the direct product is the minimum of the two arc lengths that induce it. If |directed=0|, the product graph will be an undirected graph with edges consisting of consecutive arc pairs according to the standard GraphBase conventions, and the input graphs should adhere to the same conventions. @(gb_basic.h@>= #define cartesian 0 #define direct 1 #define strong 2 @ @= Graph *product(g,gg,type,directed) Graph *g,*gg; /* graphs to be multiplied */ long type; /* |cartesian|, |direct|, or |strong| */ long directed; /* should the graph be directed? */ {@+@@;@+@q{@>@t}\6{@>@q}@> register Vertex *u,*vv; register long n; /* the number of vertices in the product graph */ if (g==NULL || gg==NULL) panic(missing_operand); /* where are |g| and |gg|? */ @; if ((type&1)==0) @; if (type) @; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* @@?`$*$\#!\&, we ran out of memory somewhere back there */ } return new_graph; } @ We must be constantly on guard against running out of memory, especially when multiplying information. The vertex names in the product are pairs of original vertex names separated by commas. Thus, for example, if you cross an |econ| graph with a |roget| graph, you can get vertices like |"Financial services, mediocrity"|. @= {@+float test_product=((float)(g->n))*((float)(gg->n)); if (test_product>MAX_NNN) panic(very_bad_specs); /* way too many vertices */ } n=(g->n)*(gg->n); new_graph=gb_new_graph(n); if (new_graph==NULL) panic(no_room); /* out of memory before we're even started */ for (u=new_graph->vertices,v=g->vertices,vv=gg->vertices;@| uvertices+n;u++) { sprintf(buffer,"%.*s,%.*s",BUF_SIZE/2-1,v->name,(BUF_SIZE-1)/2,vv->name); u->name=gb_save_string(buffer); if (++vv==gg->vertices+gg->n) vv=gg->vertices,v++; /* ``carry'' */ } sprintf(buffer,",%d,%d)",(type?2:0)-(int)(type&1),directed?1:0); make_double_compound_id(new_graph,"product(",g,",",gg,buffer); @ @= {@+register Vertex *uu,*uuu; register Arc *a; register siz_t delta; /* difference in memory addresses */ delta=((siz_t)(new_graph->vertices))-((siz_t)(gg->vertices)); for (u=gg->vertices;uvertices+gg->n;u++) for (a=u->arcs;a;a=a->next) { v=a->tip; if (!directed) { if (u>v) continue; if (u==v && a->next==a+1) a++; /* skip second half of self-loop */ } for (uu=vert_offset(u,delta),vv=vert_offset(v,delta);@| uuvertices+n;uu+=gg->n,vv+=gg->n) if (directed) gb_new_arc(uu,vv,a->len); else gb_new_edge(uu,vv,a->len); } @; } @ @= for (u=g->vertices,uu=new_graph->vertices;uuvertices+n; u++,uu+=gg->n) for (a=u->arcs;a;a=a->next) { v=a->tip; if (!directed) { if (u>v) continue; if (u==v && a->next==a+1) a++; /* skip second half of self-loop */ } vv=new_graph->vertices+((gg->n)*(v-g->vertices)); for (uuu=uu;uuun;uuu++,vv++) if (directed) gb_new_arc(uuu,vv,a->len); else gb_new_edge(uuu,vv,a->len); } @ @= {@+Vertex *uu;@+Arc *a; siz_t delta0= ((siz_t)(new_graph->vertices))-((siz_t)(gg->vertices)); siz_t del=(gg->n)*sizeof(Vertex); register siz_t delta,ddelta; for (uu=g->vertices,delta=delta0;uuvertices+g->n;uu++,delta+=del) for (a=uu->arcs;a;a=a->next) { vv=a->tip; if (!directed) { if (uu>vv) continue; if (uu==vv && a->next==a+1) a++; /* skip second half of self-loop */ } ddelta=delta0+del*(vv-g->vertices); for (u=gg->vertices;uvertices+gg->n;u++) {@+register Arc *aa; for (aa=u->arcs;aa;aa=aa->next) {@+long length=a->len; if (length>aa->len) length=aa->len; v=aa->tip; if (directed) gb_new_arc(vert_offset(u,delta),vert_offset(v,ddelta),length); else gb_new_edge(vert_offset(u,delta), vert_offset(v,ddelta),length); } } } } @* Induced graphs. Another important way to transform a graph is to remove, identify, or split some of its vertices. All of these operations are performed by the |induced| routine, which users can invoke by calling `|induced(g,description,self,multi,directed)|'. Each vertex |v| of |g| should first be assigned an ``induction code'' in its field |v->ind|, which is actually utility field~|z|. The induction code is 0~if |v| is to be eliminated; it is 1~if |v| is to be retained; it is |k>1| if |v| is to be split into $k$ nonadjacent vertices having the same neighbors as~|v| did; and it is |k<0| if |v| is to be identified with all other vertices having the same value of~|k|. For example, suppose |g| is a circuit with vertices $\{0,1,\ldots,9\}$, where |j| is adjacent to~|k| if and only if $k=(j\pm1)\bmod10$. If we set $$\vcenter{\halign{\hbox{\hfil#\hfil}\cr |0->ind=0|,\quad |1->ind=5->ind=9->ind=-1|,\quad |2->ind=3->ind=-2|,\cr |4->ind=6->ind=8->ind=1|,\quad and |7->ind=3|,\cr}}$$ the induced graph will have vertices $\{-1,-2,4,6,7,7',7'',8\}$. The vertices adjacent to 6, say, will be $-1$ (formerly~5), 7, $7'$, and~$7''$. The vertices adjacent to $-1$ will be those formerly adjacent to 1, 5, or~9, namely $-2$ (formerly~2), 4, 6, and~8. The vertices adjacent to $-2$ will be those formerly adjacent to 2 or~3, namely $-1$ (formerly~1), $-2$ (formerly~3), $-2$ (formerly~2), and~4. Duplicate edges will be discarded if |multi==0|, and self-loops will be discarded if |self==0|. The total number of vertices in the induced graph will be the sum of the positive |ind| fields plus the absolute value of the most negative |ind| field. This rule implies, for example, that if at least one vertex has |ind=-5|, the induced graph will always have a vertex $-4$, even though no |ind| field has been set to~$-4$. The |description| parameter is a string that will appear as part of the name of the induced graph; if |description=0|, this string will be empty. In the latter case, users are encouraged to assign a suitable name to the |id| field of the induced graph themselves, characterizing the method by which the |ind| codes were set. If the |directed| parameter is zero, the input graph will be assumed to be undirected, and the output graph will be undirected. When |multi=0|, the length of an arc that represents multiple arcs will be the minimum of the multiple arc lengths. @d ind z.I @(gb_basic.h@>= #define ind @[z.I /* utility field |z| when used to induce a graph */@] @ Here's a simple example: To get a complete bipartite graph with parts of sizes |n1| and |n2|, we can start with a trivial two-point graph and split its vertices into |n1| and |n2| parts. @= Graph *bi_complete(n1,n2,directed) unsigned long n1; /* size of first part */ unsigned long n2; /* size of second part */ long directed; /* should all arcs go from first part to second? */ {@+Graph *new_graph=board(2L,0L,0L,0L,1L,0L,directed); if (new_graph) { new_graph->vertices->ind=n1; (new_graph->vertices+1)->ind=n2; new_graph=induced(new_graph,NULL,0L,0L,directed); if (new_graph) { sprintf(new_graph->id,"bi_complete(%lu,%lu,%d)",@| n1,n2,directed?1:0); mark_bipartite(new_graph,n1); } } return new_graph; } @ The |induced| routine also provides a special feature not mentioned above: If the |ind| field of any vertex |v| is |IND_GRAPH| or greater (where |IND_GRAPH| is a large constant, much larger than the number of vertices that would fit in computer memory), then utility field |v->subst| should point to a graph. A copy of the vertices of that graph will then be substituted for |v| in the induced graph. This feature extends the ordinary case when |v->ind>0|, which essentially substitutes an empty graph for~|v|. If substitution is being used to replace all of $g$'s vertices by disjoint copies of some other graph~$g'$, the induced graph will be somewhat similar to a product graph. But it will not be the same as any of the three types of output produced by |product|, because the relation between $g$ and $g'$ is not symmetrical. Assuming that no self-loops are present, and that graphs $(g,g')$ have respectively $(m,m')$ arcs and $(n,n')$ vertices, the result of substituting $g'$ for all vertices of~$g$ has $m'n+mn'^2$ arcs. @d IND_GRAPH 1000000000 /* when |ind| is a billion or more, */ @d subst y.G /* we'll look at the |subst| field */ @(gb_basic.h@>= #define IND_GRAPH 1000000000 #define subst @[y.G@] @ For example, we can use the |IND_GRAPH| feature to create a ``wheel'' of $n$ vertices arranged cyclically, all connected to one or more center points. In the directed case, the arcs will run from the center(s) to a cycle; in the undirected case, the edges will join the center(s) to a circuit. @= Graph *wheel(n,n1,directed) unsigned long n; /* size of the rim */ unsigned long n1; /* number of center points */ long directed; /* should all arcs go from center to rim and around? */ {@+Graph *new_graph=board(2L,0L,0L,0L,1L,0L,directed); /* trivial 2-vertex graph */ if (new_graph) { new_graph->vertices->ind=n1; (new_graph->vertices+1)->ind=IND_GRAPH; (new_graph->vertices+1)->subst=board(n,0L,0L,0L,1L,1L,directed); /* cycle or circuit */ new_graph=induced(new_graph,NULL,0L,0L,directed); if (new_graph) { sprintf(new_graph->id,"wheel(%lu,%lu,%d)",@| n,n1,directed?1:0); } } return new_graph; } @ @(gb_basic.h@>= extern Graph *bi_complete(); extern Graph *wheel(); /* standard applications of |induced| */ @ @= Graph *induced(g,description,self,multi,directed) Graph *g; /* graph marked for induction in its |ind| fields */ char *description; /* string to be mentioned in |new_graph->id| */ long self; /* should self-loops be permitted? */ long multi; /* should multiple arcs be permitted? */ long directed; /* should the graph be directed? */ {@+@@;@+@q{@>@t}\6{@>@q}@> register Vertex *u; register long n=0; /* total number of vertices in induced graph */ register long nn=0; /* number of negative vertices in induced graph */ if (g==NULL) panic(missing_operand); /* where is |g|? */ @; @; @; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* aargh, we ran out of memory somewhere back there */ } return new_graph; } @ @= @; new_graph=gb_new_graph(n); if (new_graph==NULL) panic(no_room); /* out of memory before we're even started */ @; sprintf(buffer,",%s,%d,%d,%d)",@|description?description:null_string,@| self?1:0,multi?1:0,directed?1:0); make_compound_id(new_graph,"induced(",g,buffer); @ @= for (v=g->vertices;vvertices+g->n;v++) if (v->ind>0) { if (n>IND_GRAPH) panic(very_bad_specs); /* way too big */ if (v->ind>=IND_GRAPH) { if (v->subst==NULL) panic(missing_operand+1); /* substitute graph is missing */ n+=v->subst->n; }@+else n+=v->ind; }@+else if (v->ind<-nn) nn=-(v->ind); if (n>IND_GRAPH || nn>IND_GRAPH) panic(very_bad_specs+1); /* gigantic */ n+=nn; @ The negative vertices get the negative number as their name. Split vertices get names with an optional prime appended, if the |ind| field is 2; otherwise split vertex names are obtained by appending a colon and an index number between |0| and~|ind-1|. The name of a vertex within a graph |v->subst| is composed of the name of |v| followed by a colon and the name within that graph. We store the original |ind| field in the |mult| field of the first corresponding vertex in the new graph, and change |ind| to point to that vertex. This convention makes it easy to determine the location of each vertex's clone or clones. Of course, if the original |ind| field is zero, we leave it zero (|NULL|), because it has no corresponding vertex in the new graph. @= for (k=1,u=new_graph->vertices;k<=nn;k++,u++) { u->mult=-k; sprintf(buffer,"%ld",-k); u->name=gb_save_string(buffer); } for (v=g->vertices;vvertices+g->n;v++) if ((k=v->ind)<0) v->map=(new_graph->vertices)-(k+1); else if (k>0) { u->mult=k; v->map=u; if (k<=2) { u->name=gb_save_string(v->name); u++; if (k==2) { sprintf(buffer,"%s'",v->name); u->name=gb_save_string(buffer); u++; } }@+else if (k>=IND_GRAPH) @@; else for (j=0;jname,j); u->name=gb_save_string(buffer); } } @ @= for (v=g->vertices;vvertices+g->n;v++) if (v->map) v->ind=v->map->mult; for (v=new_graph->vertices;vvertices+n;v++) v->u.I=v->v.I=v->z.I=0; /* clear |tmp|, |mult|, |tlen| */ @ The heart of the procedure to construct an induced graph is, of course, the part where we map the arcs of |g| into arcs of |new_graph|. Notice that if |v| has a self-loop in the original graph and if |v| is being split into several vertices, it will produce arcs between different clones of itself, but it will not produce self-loops unless |self!=0|. In an undirected graph, a loop from a vertex to itself will not produce multiple edges among its clones, even if |multi!=0|. More precisely, if |v| has |k| clones |u| through |u+k-1|, an original directed arc from |v| to~|v| will generate all $k^2$ possible arcs between them, except that the |k| self-loops will be eliminated when |self==0|. An original undirected edge from |v| to~|v| will generate $k\choose2$ edges between distinct clones, together with |k| undirected self-loops if |self!=0|. @= for (v=g->vertices;vvertices+g->n;v++) { u=v->map; if (u) {@+register Arc *a;@+register Vertex *uu,*vv; k=u->mult; if (k<0) k=1; /* |k| is the number of clones of |v| */ else if (k>=IND_GRAPH) k=v->subst->n; for (;k;k--,u++) { if (!multi) @; for (a=v->arcs;a;a=a->next) { vv=a->tip; uu=vv->map; if (uu==NULL) continue; j=uu->mult; if (j<0) j=1; /* |j| is the number of clones of |vv| */ else if (j>=IND_GRAPH) j=vv->subst->n; if (!directed) { if (vvnext==a+1) a++; /* skip second half of self-loop */ j=k,uu=u; /* also skip duplicate edges generated by self-loop */ } } @; } } } } @ Again we use the |tmp| and |tlen| trick of |gunion| to handle multiple arcs. (This trick explains why the code in the previous section tries to generate as many arcs as possible from a single vertex |u|, before changing~|u|.) @= for (a=u->arcs;a;a=a->next) { a->tip->tmp=u; if (directed || a->tip>u || a->next==a+1) a->tip->tlen=a; else a->tip->tlen=a+1; } @ @= for (;j;j--,uu++) { if (u==uu && !self) continue; if (uu->tmp==u && !multi) @; if (directed) gb_new_arc(u,uu,a->len); else gb_new_edge(u,uu,a->len); uu->tmp=u; uu->tlen=((directed || u<=uu)? u->arcs: uu->arcs); } @ @= {@+register Arc *b=uu->tlen; /* existing arc or edge from |u| to |uu| */ if (a->lenlen) { b->len=a->len; /* remember the minimum length */ if (!directed) (b+1)->len=a->len; } continue; } @ We have now accumulated enough experience to finish off the one remaining piece of program with ease. @= {@+register Graph *gg=v->subst; register Vertex *vv=gg->vertices; register Arc *a; siz_t delta=((siz_t)u)-((siz_t)vv); for (j=0;jsubst->n;j++,u++,vv++) { sprintf(buffer,"%.*s:%.*s",BUF_SIZE/2-1,v->name,(BUF_SIZE-1)/2,vv->name); u->name=gb_save_string(buffer); for (a=vv->arcs;a;a=a->next) {@+register Vertex *vvv=a->tip; Vertex *uu=vert_offset(vvv,delta); if (vvv==vv && !self) continue; if (uu->tmp==u && !multi) @; if (!directed) { if (vvvnext==a+1) a++; /* skip second half of self-loop */ gb_new_edge(u,uu,a->len); }@+else gb_new_arc(u,uu,a->len); uu->tmp=u; uu->tlen=((directed || u<=uu)? u->arcs: uu->arcs); } } } @* Index. As usual, we close with an index that shows where the identifiers of \\{gb\_basic} are defined and used. gb_books.w0000444000175000017500000005633210601506304011157 0ustar dondon% 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\_\,BOOKS} \def\<#1>{\hbox{$\langle$\rm#1$\rangle$}} \prerequisites{GB\_\,GRAPH}{GB\_\,IO} @* Introduction. This GraphBase module contains the |book| subroutine, which creates a family of undirected graphs that are based on classic works of literature. It also contains the |bi_book| subroutine, which creates a related family of bipartite graphs. An example of the use of |book| can be found in the demonstration program {\sc BOOK\_\kern.05emCOMPONENTS}. @(gb_books.h@>= extern Graph *book(); extern Graph *bi_book(); @ The subroutine call |book(@[@t\@>@],n,x,first_chapter,last_chapter, in_weight,out_weight,seed)| constructs a graph based on the information in \<title>\.{.dat}, where \<title> is either \.{"anna"} (for {\sl Anna Karenina\/}), \.{"david"} (for {\sl David Copperfield\/}), \.{"jean"} (for {\sl Les Mis\'erables\/}), \.{"huck"} (for {\sl Huckleberry Finn\/}), or \.{"homer"} (for {\sl The Iliad\/}). Each vertex of the graph corresponds to one of the characters in the selected book. Edges between vertices correspond to encounters between those characters. The length of each edge is~1. Subsets of the book can be selected by specifying that the edge data should be restricted to chapters between |first_chapter| and |last_chapter|, inclusive. If |first_chapter=0|, the result is the same as if |first_chapter=1|. If |last_chapter=0| or if |last_chapter| exceeds the total number of chapters in the book, the result is the same as if |last_chapter| were the number of the book's final chapter. The constructed graph will have $\min(n,N)-x$ vertices, where $N$ is the total number of characters in the selected book. However, if |n| is zero, |n| is automatically made equal to the maximum possible value,~$N$. If |n| is less than~$N$, the |n-x| characters will be selected by assigning a weight to each character and choosing the |n| with largest weight, then excluding the largest~|x| of these, using random numbers to break ties in case of equal weights. Weights are computed by the formula $$ |in_weight|\cdot\\{chapters\_in}+|out_weight|\cdot\\{chapters\_out}, $$ where \\{chapters\_in} is the number of chapters between |first_chapter| and |last_chapter| in which a particular character appears, and \\{chapters\_out} is the number of other chapters in which that character appears. Both |in_weight| and |out_weight| must be at most 1,000,000 in absolute value. Vertices of the graph will appear in order of decreasing weight. The |seed| parameter defines the pseudo-random numbers used wherever a ``random'' choice between equal-weight vertices needs to be made. As usual with GraphBase routines, different choices of |seed| will in general produce different selections, but in a system-independent manner; identical results will be obtained on all computers when identical parameters have been specified. Any |seed| value between 0 and $2^{31}-1$ is permissible. @ Examples: The call |book("anna",0,0,0,0,0,0,0)| will construct a graph on 138 vertices that represent all 138 characters of Tolstoy's {\sl Anna Karenina\/}, as recorded in \.{anna.dat}. Two vertices will be adjacent if the corresponding characters encounter each other anywhere in the book. The call |book("anna",50,0,0,0,1,1,0)| is similar, but it is restricted to the 50 characters that occur most frequently, i.e., in the most chapters. The call |book("anna",50,0,10,120,1,1,0)| has the same vertices, but it has edges only for encounters that take place between chapter~10 and chapter~120, inclusive. The call |book("anna",50,0,10,120,1,0,0)| is similar, but its vertices are the 50 characters that occur most often in chapters 10 through~120, without regard to how often they occur in the rest of the book. The call |book("anna",50,0,10,120,0,0,0)| is also similar, but it chooses 50 characters completely at random (possibly from those that don't occur in the selected chapters at all). Parameter |x|, which causes the |x| vertices of highest weight to be excluded, is usually either 0 or~1. It is provided primarily so that users can set |x=1| with respect to {\sl David Copperfield\/} and {\sl Huckleberry Finn}; those novels are narrated by their principal character, so they have edges between the principal character and almost everybody else. (Characters cannot get into the action of a first-person account unless they encounter the narrator or unless the narrator is quoting some other person's story.) The corresponding graphs tend to have more interesting connectivity properties if we leave the narrator out by setting |x=1|. For example, there are 87 characters in {\sl David Copperfield\/}; the call |book("david",0,1,0,0,1,1,0)| produces a graph with 86 vertices, one for every character except David Copperfield himself. @ The subroutine call |bi_book(@[@t\<title>@>@],n,x,first_chapter,last_chapter, in_weight,out_weight,seed)| produces a bipartite graph in which the vertices of the first part are exactly the same as the vertices of the graph returned by |book|, while the vertices of the second part are the selected chapters. For example, $|bi_book|(|"anna"|,\allowbreak 50,0,10,120,1,1,0)$ creates a bipartite graph with $50+111$ vertices. There is an edge between each character and the chapters in which that character appears. @ Chapter numbering needs further explanation. {\sl Anna Karenina\/} has 239 chapters, which are numbered 1.1 through 8.19 in the work itself but renumbered 1 through 239 as far as the |book| routine is concerned. Thus, setting |first_chapter=10| and |last_chapter=120| turns out to be equivalent to selecting chapters 1.10 through 4.19 (more precisely, chapter~10 of book~1 through chapter~19 of book~4). {\sl Les Mis\'erables\/} has an even more involved scheme; its 356 chapters range from 1.1.1 (part~1, book~1, chapter~1) to 5.9.6 (part~5, book~9, chapter~6). After |book| or |bi_book| has created a graph, the external integer variable |chapters| will contain the total number of chapters, and |chap_name| will be an array of strings containing the structured chapter numbers. For example, after |book("jean",@[@t\dots@>@])|, we will have |chapters=356|, |chap_name[1]="1.1.1"|, \dots, |chap_name[356]="5.9.6"|; |chap_name[0]| will be~|""|. @d MAX_CHAPS 360 /* no book will have this many chapters */ @<External variables@>= long chapters; /* the total number of chapters in the selected book */ char *chap_name[MAX_CHAPS]={""}; /* string names of those chapters */ @ As usual, we put declarations of the external variables into the header file for users to {\bf include}. @(gb_books.h@>= extern long chapters; /* the total number of chapters in the selected book */ extern char *chap_name[]; /* string names of those chapters */ @ If the |book| or |bi_book| routine encounters a problem, it returns |NULL| (\.{NULL}), after putting a code number into the external variable |panic_code|. This code number identifies the type of failure. Otherwise |book| 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_trouble_code=0;@+return NULL;@+} @# @f node long /* the \&{node} type is defined below */ @ The \CEE/ file \.{gb\_books.c} has the overall shape shown here. It makes use of an internal subroutine called |bgraph|, which combines the work of |book| and |bi_book|. @p #include "gb_io.h" /* we will use the {\sc GB\_\,IO} routines for input */ #include "gb_flip.h" /* we will use the {\sc GB\_\,FLIP} routines for random numbers */ #include "gb_graph.h" /* we will use the {\sc GB\_\,GRAPH} data structures */ #include "gb_sort.h" /* and the |gb_linksort| routine */ @h@# @<Type declarations@>@; @<Private variables@>@; @<External variables@>@; @# static Graph *bgraph(bipartite, title,n,x,first_chapter,last_chapter,in_weight,out_weight,seed) long bipartite; /* should we make the graph bipartite? */ char *title; /* identification of the selected book */ unsigned long n; /* number of vertices desired before exclusion */ unsigned long x; /* number of vertices to exclude */ unsigned long first_chapter, last_chapter; /* interval of chapters leading to edges */ long in_weight; /* weight coefficient pertaining to chapters in that interval */ long out_weight; /* weight coefficient pertaining to chapters not in that interval */ long seed; /* random number seed */ {@+@<Local variables@>@;@# gb_init_rand(seed); @<Check that the parameters are valid@>; @<Skim the data file, recording the characters and computing their statistics@>; @<Choose the vertices and put them into an empty graph@>; @<Read the data file more carefully and fill the graph as instructed@>; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* (expletive deleted) we ran out of memory somewhere back there */ } return new_graph; } @# Graph *book(title,n,x,first_chapter,last_chapter,in_weight,out_weight,seed) char *title; unsigned long n, x, first_chapter, last_chapter; long in_weight,out_weight,seed; {@+return bgraph(0L,title,n,x,first_chapter,last_chapter, in_weight,out_weight,seed);@+} Graph *bi_book(title,n,x,first_chapter,last_chapter,in_weight,out_weight,seed) char *title; unsigned long n, x, first_chapter, last_chapter; long in_weight,out_weight,seed; {@+return bgraph(1L,title,n,x,first_chapter,last_chapter, in_weight,out_weight,seed);@+} @ @<Local var...@>= Graph *new_graph; /* the graph constructed by |book| or |bi_book| */ register long j,k; /* all-purpose indices */ long characters; /* the total number of characters in the selected book */ register node *p; /* information about the current character */ @ @d MAX_CHARS 600 /* there won't be more characters than this */ @<Check that the parameters are valid@>= if (n==0) n=MAX_CHARS; if (first_chapter==0) first_chapter=1; if (last_chapter==0) last_chapter=MAX_CHAPS; if (in_weight>1000000 || in_weight<-1000000 || out_weight>1000000 || out_weight<-1000000) panic(bad_specs); /* the magnitude of at least one weight is too big */ sprintf(file_name,"%.6s.dat",title); if (gb_open(file_name)!=0) panic(early_data_fault); /* couldn't open the file; |io_errors| tells why */ @ @<Priv...@>= static char file_name[]="xxxxxx.dat"; @*Vertices. Each character in a book has been given a two-letter code name for internal use. The code names are explained at the beginning of each data file by a number of lines that look like this: $$\hbox{\tt XX \<name>,\<description>}$$ For example, here's one of the lines near the beginning of |"anna.dat"|: $$\hbox{\tt AL Alexey Alexandrovitch Karenin, minister of state}$$ The \<name> does not contain a comma; the \<description> might. A blank line follows the cast of characters. Internally, we will think of the two-letter code as a radix-36 integer. Thus \.{AA} will be the number $10\times36+10$, and \.{ZZ} will be $35\times36+35$. The |gb_number| routine in {\sc GB\_\,IO} is set up to input radix-36 integers just as it does hexadecimal ones. In {\sl The Iliad}, many of the minor characters have numeric digits in their code names because the total number of characters is too large to permit mnemonic codes for everybody. @d MAX_CODE 1296 /* $36\times36$, the number of two-digit codes in radix 36 */ @ In order to choose the vertices, we want to represent each character as a node whose key corresponds to its weight; then the |gb_linksort| routine of {\sc GB\_\,SORT} will provide the desired rank-ordering. We will find it convenient to use these nodes for all the data processing that |bgraph| has to do. @<Type dec...@>= typedef struct node_struct { /* records to be sorted by |gb_linksort| */ long key; /* the nonnegative sort key (weight plus $2^{30}$) */ struct node_struct *link; /* pointer to next record */ long code; /* code number of this character */ long in; /* number of occurrences in selected chapters */ long out; /* number of occurrences in unselected chapters */ long chap; /* seen most recently in this chapter */ Vertex *vert; /* vertex corresponding to this character */ } node; @ Not only do nodes point to codes, we also want codes to point to nodes. @<Priv...@>= static node node_block[MAX_CHARS]; /* array of nodes for working storage */ static node *xnode[MAX_CODE]; /* the node, if any, having a given code */ @ We will read the data file twice, once quickly (to collect statistics) and once more thoroughly (to record detailed information). Here is the quick version. @<Skim the data file, recording the characters...@>= @<Read the character codes at the beginning of the data file, and prepare a node for each one@>; @<Skim the chapter information, counting the number of chapters in which each character appears@>; if (gb_close()!=0) panic(late_data_fault); /* checksum or other failure in data file; see |io_errors| */ @ @<Read the character codes...@>= for (k=0;k<MAX_CODE;k++) xnode[k]=NULL; {@+register long c; /* current code entering the system */ p=node_block; /* current node entering the system */ while ((c=gb_number(36))!=0) { /* note that \.{00} is not a legal code */ if (c>=MAX_CODE || gb_char()!=' ') panic(syntax_error); /* unreadable line in data file */ if (p>=&node_block[MAX_CHARS]) panic(syntax_error+1); /* data has too many characters */ p->link=(p==node_block?NULL:p-1); p->code=c; xnode[c]=p; p->in=p->out=p->chap=0; p->vert=NULL; p++; gb_newline(); } characters=p-node_block; gb_newline(); /* bypass the blank line that terminates the character data */ } @ Later we will read through this part of the file again, extracting additional information if it turns out to be relevant. The \<description> string is provided to users in a |desc| field, in case anybody cares to look at it. The |in| and |out| statistics are also made available in utility fields called |in_count| and |out_count|. The code value is placed in the |short_code| field. @d desc z.S /* utility field |z| points to the \<description> string */ @d in_count y.I /* utility field |y| counts appearances in selected chapters */ @d out_count x.I /* utility field |x| counts appearances in other chapters */ @d short_code u.I /* utility field |u| contains a radix-36 number */ @<Read the data about characters again, noting vertex names and the associated descriptions@>= {@+register long c; /* current code entering the system a second time */ while ((c=gb_number(36))!=0) {@+register Vertex *v=xnode[c]->vert; if (v) { if (gb_char()!=' ') panic(impossible); /* can't happen */ gb_string(str_buf,','); /* scan the \<name> part */ v->name=gb_save_string(str_buf); if (gb_char()!=',') panic(syntax_error+2); /* missing comma after \<name> */ if (gb_char()!=' ') panic(syntax_error+3); /* missing space after comma */ gb_string(str_buf,'\n'); /* scan the \<description> part */ v->desc=gb_save_string(str_buf); v->in_count=xnode[c]->in; v->out_count=xnode[c]->out; v->short_code=c; } gb_newline(); } gb_newline(); /* bypass the blank line that terminates the character data */ } @ @(gb_books.h@>= #define desc @t\quad@> z.S /* utility field definitions for the header file */ #define in_count @t\quad@> y.I #define out_count @t\quad@> x.I #define short_code @t\quad@> u.I @*Edges. The second part of the data file has a line for each chapter, containing ``cliques of encounters.'' For example, the line $$\hbox{\tt3.22:AA,BB,CC,DD;CC,DD,EE;AA,FF}$$ means that, in chapter 22 of book 3, there were encounters between the pairs $$\def\\{{\rm,} } \hbox{\tt AA-BB\\AA-CC\\AA-DD\\BB-CC\\BB-DD\\CC-DD\\CC-EE\\DD-EE\\{\rm and }% AA-FF\rm.}$$ (The encounter \.{CC-DD} is specified twice, once in the clique \.{AA,BB,CC,DD} and once in \.{CC,DD,EE}; this does not imply anything about the actual number of encounters between \.{CC} and \.{DD} in the chapter.) A clique might involve one character only, when that character is featured in sort of a soliloquy. A chapter might contain no references to characters at all. In such a case the `\.:' following the chapter number is omitted. There might be more encounters than will fit on a single line. In such cases, continuation lines begin with `\.{\&:}'. This convention turns out to be needed only in \.{homer.dat}; chapters in {\sl The Iliad\/} are substantially more complex than the chapters in other GraphBase books. On our first pass over the data, we simply want to compute statistics about who appears in what chapters, so we ignore the distinction between commas and semicolons. @<Skim the chapter information, counting the number of chapters in which each character appears@>= for (k=1; k<MAX_CHAPS && !gb_eof(); k++) { gb_string(str_buf,':'); /* read past the chapter number */ if (str_buf[0]=='&') k--; /* continuation of previous chapter */ while (gb_char()!='\n') {@+register long c=gb_number(36); if (c>=MAX_CODE) panic(syntax_error+4); /* missing punctuation between characters */ p=xnode[c]; if (p==NULL) panic(syntax_error+5); /* unknown character */ if (p->chap!=k) { p->chap=k; if (k>=first_chapter && k<=last_chapter) p->in++; else p->out++; } } gb_newline(); } if (k==MAX_CHAPS) panic(syntax_error+6); /* too many chapters */ chapters=k-1; @ Our second pass over the data is very similar to the first, if we are simply computing a bipartite graph. In that case we add an edge to the graph between each selected chapter and each selected character in that chapter. Local variable |chap_base| will point to a vertex such that |chap_base+k| is the vertex corresponding to chapter~|k|. The |in_count| of a chapter vertex is the degree of that vertex, i.e., the number of selected characters that appear in the corresponding chapter. The |out_count| is the number of characters that appear in the chapter but were omitted from the graph. Thus the |in_count| and |out_count| for chapters are analogous to the |in_count| and |out_count| for characters. @<Read the chapter information a second time and create the appropriate bipartite edges@>= { for (p=node_block;p<node_block+characters;p++) p->chap=0; for (k=1; !gb_eof(); k++) { gb_string(str_buf,':'); /* read the chapter number */ if (str_buf[0]=='&') k--; else { if (str_buf[strlen(str_buf)-1]=='\n') str_buf[strlen(str_buf)-1]='\0'; chap_name[k]=gb_save_string(str_buf); } if (k>=first_chapter && k<=last_chapter) {@+register Vertex *u=chap_base+k; if (str_buf[0]!='&') { u->name=chap_name[k]; u->desc=null_string; u->in_count=u->out_count=0; } while (gb_char()!='\n') {@+register long c=gb_number(36); p=xnode[c]; if (p->chap!=k) {@+register Vertex *v=p->vert; p->chap=k; if (v) {@+ gb_new_edge(v,u,1L); u->in_count++; }@+else u->out_count++; } } } gb_newline(); } } @ @<Local variables@>= Vertex *chap_base; /* the bipartite vertex for chapter~|k| is |chap_base+k| */ @ The second pass has to work a little harder when we are recording encounters from cliques, but the logic isn't difficult really. We insert a reference to the first chapter that generated each edge, in utility field |chap_no| of the corresponding |Arc| record. @d chap_no a.I /* utility field |a| holds a chapter number */ @<Read the chapter information a second time and create the appropriate edges for encounters@>= for (k=1; !gb_eof(); k++) {@+char *s; s=gb_string(str_buf,':'); /* read the chapter number */ if (str_buf[0]=='&') k--; else {@+if (*(s-2)=='\n') *(s-2)='\0'; chap_name[k]=gb_save_string(str_buf); } if (k>=first_chapter && k<=last_chapter) {@+register long c=gb_char(); while (c!='\n') {@+register Vertex **pp=clique_table; register Vertex **qq,**rr; /* pointers within the clique table */ do@+{@+ c=gb_number(36); /* set |c| to code for next character of clique */ if (xnode[c]->vert) /* is that character a selected vertex? */ *pp++=xnode[c]->vert; /* if so, that vertex joins the current clique */ c=gb_char(); }@+while (c==','); /* repeat until end of the clique */ for (qq=clique_table;qq+1<pp;qq++) for (rr=qq+1;rr<pp;rr++) @<Make the vertices |*qq| and |*rr| adjacent, if they aren't already@>; } } gb_newline(); } @ @(gb_books.h@>= #define chap_no @[a.I@] /* utility field definition in the header file */ @ @<Priv...@>= static Vertex *clique_table[30]; /* pointers to vertices in the current clique */ @ @<Make the vertices |*qq| and |*rr| adjacent...@>= {@+register Vertex *u=*qq, *v=*rr; register Arc *a; for (a=u->arcs; a; a=a->next) if (a->tip==v) goto found; gb_new_edge(u,v,1L); /* not found, so they weren't already adjacent */ if (u<v) a=u->arcs; else a=v->arcs; /* the new edge consists of arcs |a| and |a+1| */ a->chap_no=(a+1)->chap_no=k; found:; } @*Administration. The program is now complete except for a few missing organizational details. I will add these after lunch. @^out to lunch@> @ OK, I'm back; what needs to be done? The main thing is to create the graph itself. @<Choose the vertices and put them into an empty graph@>= if (n>characters) n=characters; if (x>n) x=n; if (last_chapter>chapters) last_chapter=chapters; if (first_chapter>last_chapter) first_chapter=last_chapter+1; new_graph=gb_new_graph(n-x+(bipartite?last_chapter-first_chapter+1:0)); if (new_graph==NULL) panic(no_room); /* out of memory already */ strcpy(new_graph->util_types,"IZZIISIZZZZZZZ"); /* declare the types of utility fields */ sprintf(new_graph->id,"%sbook(\"%s\",%lu,%lu,%lu,%lu,%ld,%ld,%ld)", bipartite?"bi_":"",title,n,x,first_chapter,last_chapter, in_weight,out_weight,seed); if (bipartite) { mark_bipartite(new_graph,n-x); chap_base=new_graph->vertices+(new_graph->n_1-first_chapter); } @<Compute the weights and assign vertices to chosen nodes@>; @ @<Compute the weights and assign vertices to chosen nodes@>= for (p=node_block; p<node_block+characters; p++) p->key=in_weight*(p->in)+out_weight*(p->out)+0x40000000; gb_linksort(node_block+characters-1); k=n; /* we will look at this many nodes */ {@+register Vertex *v=new_graph->vertices; /* the next vertex to define */ for (j=127; j>=0; j--) for (p=(node*)gb_sorted[j]; p; p=p->link) { if (x>0) x--; /* ignore this node */ else p->vert=v++; /* choose this node */ if (--k==0) goto done; } } done:; @ Once the graph is there, we're ready to fill it in. @<Read the data file more carefully and fill the graph as instructed@>= if (gb_open(file_name)!=0) panic(impossible+1); /* this can't happen, because we were successful before */ @<Read the data about characters again, noting vertex names and the associated descriptions@>; if (bipartite) @<Read the chapter information a second time and create the appropriate bipartite edges@>@; else @<Read the chapter information a second time and create the appropriate edges for encounters@>; if (gb_close()!=0) panic(impossible+2); /* again, can hardly happen the second time around */ @* Index. As usual, we close with an index that shows where the identifiers of \\{gb\_books} are defined and used. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gb_econ.w�������������������������������������������������������������������������������������������0000444�0001750�0001750�00000066761�07160213312�010775� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_\,ECON} \prerequisites{GB\_\,GRAPH}{GB\_\,IO} @* Introduction. This GraphBase module contains the |econ| subroutine, which creates a family of directed graphs related to the flow of money between industries. An example of the use of this procedure can be found in the demo program {\sc ECON\_\,ORDER}. @(gb_econ.h@>= extern Graph *econ(); @ The subroutine call |econ(n,omit,threshold,seed)| constructs a directed graph based on the information in \.{econ.dat}. Each vertex of the graph corresponds to one of 81 sectors of the U.S. economy. The data values come from the year 1985; they were derived from tables published in {\sl Survey of Current Business\/ \bf70} (1990), 41--56. If |omit=threshold=0|, the directed graph is a ``circulation''; that is, each arc has an associated |flow| value, and the sum of arc flows leaving each vertex is equal to the sum of arc flows entering. This sum is called the ``total commodity output'' for the sector in question. The flow in an arc from sector $j$~to sector~$k$ is the amount of the commodity made by sector~$j$ that was used by sector~$k$, rounded to millions of dollars at producers' prices. For example, the total commodity output of the sector called \.{Apparel} is 54031, meaning that the total cost of making all kinds of apparel in 1985 was about 54 billion dollars. There is an arc from \.{Apparel} to itself with a flow of 9259, meaning that 9.259 billion dollars' worth of apparel went from one group within the apparel industry to another. There also is an arc of flow~44 from \.{Apparel} to \.{Household} \.{furniture}, indicating that some 44 million dollars' worth of apparel went into the making of household furniture. By looking at all arcs that leave the \.{Apparel} vertex, you can see where all that new apparel went; by looking at all arcs that enter \.{Apparel}, you can see what ingredients the apparel industry needed to make~it. One vertex, called \.{Users}, represents people like you and me, the non-industrial end users of everything. The arc from \.{Apparel} to \.{Users} has flow 42172; this is the ``total final demand'' for apparel, the amount that didn't flow into other sectors of the economy before it reached people like us. The arc from \.{Users} to \.{Apparel} has flow 19409, which is called the ``value added'' by users; it represents wages and salaries paid to support the manufacturing process. The sum of total final demand over all sectors, which also equals the sum of value added over all sectors, is conventionally called the Gross National Product (GNP). In 1985 the GNP was 3999362, nearly 4 trillion dollars, according to \.{econ.dat}. (The sum of all arc flows coming out of all vertices was 7198680; this sum overestimates the total economic activity, because it counts some items more than once---statistics are recorded whenever an item passes a statistics gatherer. Economists try to adjust the data so that they avoid double-counting as much as possible.) Speaking of economists, there is another special vertex called \.{Adjustments}, included by economists so that GNP is measured more accurately. This vertex takes account of such things as changes in the value of inventories, and imported materials that cannot be obtained within the U.S., as well as work done for the government and for foreign concerns. In 1985, these adjustments accounted for about 11\% of the GNP. Incidentally, some of the ``total final demand'' arcs are negative. For example, the arc from \.{Petroleum} \.{and} \.{natural} \.{gas} \.{production} to \.{Users} has flow $-27032$. This might seem strange at first, but it makes sense when imports are considered, because crude oil and natural gas go more to other industries than to end users. Total final demand does not mean total user demand. @d flow a.I /* utility field |a| specifies the flow in an arc */ @ If |omit=1|, the \.{Users} vertex is omitted from the digraph; in particular, this will eliminate all arcs of negative flow. If |omit=2|, the \.{Adjustments} vertex is also omitted, thereby leaving 79~sectors with arcs showing inter-industry flow. (The graph is no longer a ``circulation,'' of course, when |omit>0|.) If \.{Users} and \.{Adjustments} are not omitted, \.{Users} is the last vertex of the graph, and \.{Adjustments} is next-to-last. If |threshold=0|, the digraph has an arc for every nonzero |flow|. But if |threshold>0|, the digraph becomes more sparse; there is then an arc from $j$ to~$k$ if and only if the amount of commodity $j$ used by sector~$k$ exceeds |threshold/65536| times the total input of sector~$k$. (The total input figure always includes value added, even if |omit>0|.) Thus the arcs go to each sector from that sector's main suppliers. When |n=79|, |omit=2|, and |threshold=0|, the digraph has 4602 arcs out of a possible $79\times79=6241$. Raising |threshold| to 1 decreases the number of arcs to 4473; raising it to 6000 leaves only~72 arcs. The |len| field in each arc is~1. The constructed graph will have $\min(n,81-|omit|)$ vertices. If |n| is less than |81-omit|, the |n| vertices will be selected by repeatedly combining related sectors. For example, two of the 81 original sectors are called `\.{Paper} \.{products,} \.{except} \.{containers}' and `\.{Paperboard} \.{containers} \.{and} \.{boxes}'; these might be combined into a sector called `\.{Paper} \.{products}'. There is a binary tree with 79 leaves, which describes a fixed hierarchical breakdown of the 79 non-special sectors. This tree is pruned, if necessary, by replacing pairs of leaves by their parent node, which becomes a new leaf; pruning continues until just |n| leaves remain. Although pruning is a bottom-up process, its effect can also be obtained from the top down if we imagine ``growing'' the tree, starting out with a whole economy as a single sector and repeatedly subdividing a sector into two parts. For example, if |omit=2| and |n=2|, the two sectors will be called \.{Goods} and \.{Services}. If |n=3|, \.{Goods} might be subdivided into \.{Natural} \.{Resources} and \.{Manufacturing}; or \.{Services} might be subdivided into \.{Indirect} \.{Services} and \.{Direct} \.{Services}. If |seed=0|, the binary tree is pruned in such a way that the |n| resulting sectors are as equal as possible with respect to total input and output, while respecting the tree structure. If |seed>0|, the pruning is carried out at random, in such a way that all |n|-leaf subtrees of the original tree are obtained with approximately equal probability (depending on |seed| in a machine-independent fashion). Any |seed| value from 1 to $2^{31}-1=2147483647$ is permissible. As usual in GraphBase routines, you can set |n=0| to get the default situation where |n| has its maximum value. For example, either |econ(0,0,0,0)| or |econ(81,0,0,0)| produces the full graph; |econ(0,2,0,0)| or |econ(79,2,0,0)| produces the full graph except for the two special vertices. @d MAX_N 81 /* maximum number of vertices in constructed graph */ @d NORM_N MAX_N-2 /* the number of normal SIC sectors */ @d ADJ_SEC MAX_N-1 /* code number for the \.{Adjustments} sector */ @ The U.S. Bureau of Economic Analysis and the U.S. Bureau of the Census have assigned code numbers 1--79 to the individual sectors for which statistics are given in \.{econ.dat}. These sector numbers are traditionally called Standard Industrial Classification (SIC) codes. If for some reason you want to know the SIC codes for all sectors represented by vertex |v| of a graph generated by |econ|, you can access them via a list of |Arc| nodes starting at the utility field |v->SIC_codes|. This list is linked by |next| fields in the usual way, and each SIC code appears in the |len| field; the |tip| field is unused. The special vertex \.{Adjustments} is given code number~80; it is actually a composite of six different SIC categories, numbered 80--86 in their published tables. For example, if |n=80| and |omit=1|, each list will have length~1. Hence |v->SIC_codes->next| will equal |NULL| for each~|v|, and |v->SIC_codes->len| will be |v|'s SIC code, a number between 1 and~80. The special vertex \.{Users} has no SIC code; it is the only vertex whose |SIC_codes| field will be null in the graph returned by |econ|. @d SIC_codes z.A /* utility field |z| leads to the SIC codes for a vertex */ @ The total output of each sector, which also equals the total input of that sector, is placed in utility field |sector_total| of the corresponding vertex. @d sector_total y.I /* utility field |y| holds the total flow in and out */ @(gb_econ.h@>= #define flow @t\quad@> a.I /* definitions of utility fields in the header file */ #define SIC_codes @t\quad@> z.A #define sector_total @t\quad@> y.I @ If the |econ| routine encounters a problem, it returns |NULL| (\.{NULL}), after putting a nonzero number into the external variable |panic_code|. This code number identifies the type of failure. Otherwise |econ| 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_trouble_code=0;@+return NULL;@+} @ The \CEE/ file \.{gb\_econ.c} has the following overall shape: @p #include "gb_io.h" /* we will use the {\sc GB\_\,IO} routines for input */ #include "gb_flip.h" /* we will use the {\sc GB\_\,FLIP} routines for random numbers */ #include "gb_graph.h" /* and of course we'll use the {\sc GB\_\,GRAPH} data structures */ @h@# @<Type declarations@>@; @<Private variables@>@; @# Graph *econ(n,omit,threshold,seed) unsigned long n; /* number of vertices desired */ unsigned long omit; /* number of special vertices to omit */ unsigned long threshold; /* minimum per-64K-age in arcs leading in */ long seed; /* random number seed */ {@+@<Local variables@>@;@# gb_init_rand(seed); init_area(working_storage); @<Check the parameters and adjust them for defaults@>; @<Set up a graph with |n| vertices@>; @<Read \.{econ.dat} and note the binary tree structure@>; @<Determine the |n| sectors to use in the graph@>; @<Put the appropriate arcs into the graph@>; if (gb_close()!=0) panic(late_data_fault); /* something's wrong with |"econ.dat"|; see |io_errors| */ gb_free(working_storage); if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } return new_graph; } @ @<Local var...@>= Graph *new_graph; /* the graph constructed by |econ| */ register long j,k; /* all-purpose indices */ Area working_storage; /* tables needed while |econ| does its thinking */ @ @<Check the param...@>= if (omit>2) omit=2; if (n==0 || n>MAX_N-omit) n=MAX_N-omit; else if (n+omit<3) omit=3-n; /* we need at least one normal sector */ if (threshold>65536) threshold=65536; @ @<Set up a graph with |n| vertices@>= 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,"econ(%lu,%lu,%lu,%ld)",n,omit,threshold,seed); strcpy(new_graph->util_types,"ZZZZIAIZZZZZZZ"); @* The economic tree. As we read in the data, we construct a sequential list of nodes, each of which represents either a micro-sector of the economy (one of the basic SIC sectors) or a macro-sector (which is the union of two subnodes). In more technical terms, the nodes form an extended binary tree, whose external nodes correspond to micro-sectors and whose internal nodes correspond to macro-sectors. The nodes of the tree appear in preorder. Subsequently we will do a variety of operations on this binary tree, proceeding either top-down (from the beginning of the list to the end) or bottom-up (from the end to the beginning). Each node is a rather large record, because we will store a complete vector of sector output data in each node. @<Type declarations@>= typedef struct node_struct { /* records for micro- and macro-sectors */ struct node_struct *rchild; /* pointer to right child of macro-sector */ char title[44]; /* |"Sector name"| */ long table[MAX_N+1]; /* outputs from this sector */ unsigned long total; /* total input to this sector ($=$ total output) */ long thresh; /* |flow| must exceed |thresh| in arcs to this sector */ long SIC; /* SIC code number; initially zero in macro-sectors */ long tag; /* 1 if this node will be a vertex in the graph */ struct node_struct *link; /* next smallest unexplored sector */ Arc *SIC_list; /* first item on list of SIC codes */ } node; @ When we read the given data in preorder, we'll need a stack to remember what nodes still need to have their |rchild| pointer filled in. (There is a no need for an \\{lchild} pointer, because the left child always follows its parent immediately in preorder.) @<Private v...@>= static node *stack[NORM_N+NORM_N]; static node **stack_ptr; /* current position in |stack| */ static node *node_block; /* array of nodes, specifies the tree in preorder */ static node *node_index[MAX_N+1]; /* which node has a given SIC code */ @ @<Local v...@>= register node *p,*pl,*pr; /* current node and its children */ register node *q; /* register for list manipulation */ @ @<Read \.{econ.dat} and note the binary tree structure@>= node_block=gb_typed_alloc(2*MAX_N-3,node,working_storage); if (gb_trouble_code) panic(no_room+1); /* no room to copy the data */ if (gb_open("econ.dat")!=0) panic(early_data_fault); /* couldn't open |"econ.dat"| using GraphBase conventions */ @<Read and store the sector names and SIC numbers@>; for (k=1; k<=MAX_N; k++) @<Read and store the output coefficients for sector |k|@>; @ The first part of \.{econ.dat} specifies the nodes of the binary tree in preorder. Each line contains a node name followed by a colon, and the colon is followed by the SIC number if that node is a leaf. The tree is uniquely specified in this way, because of the nature of preorder. (Think of Polish prefix notation, in which a formula like `${+}x{+}xx$' means `${+}(x,{+}(x,x))$'; the parentheses in Polish notation are redundant.) The two special sector names don't appear in the file; we manufacture them ourselves. The program here is careful not to clobber itself in the presence of arbitrarily garbled data. @<Read and store the sector names...@>= stack_ptr=stack; for (p=node_block; p<node_block+NORM_N+NORM_N-1; p++) {@+register long c; gb_string(p->title,':'); if (strlen(p->title)>43) panic(syntax_error); /* sector name too long */ if (gb_char()!=':') panic(syntax_error+1); /* missing colon */ p->SIC=c=gb_number(10); if (c==0) /* macro-sector */ *stack_ptr++=p; /* left child is |p+1|, we'll know |rchild| later */ else { /* micro-sector; |p+1| will be somebody's right child */ node_index[c]=p; if (stack_ptr>stack) (*--stack_ptr)->rchild=p+1; } if (gb_char()!='\n') panic(syntax_error+2); /* garbage on the line */ gb_newline(); } if (stack_ptr!=stack) panic(syntax_error+3); /* tree malformed */ for (k=NORM_N;k;k--) if (node_index[k]==0) panic(syntax_error+4); /* SIC code not mentioned in the tree */ strcpy(p->title,"Adjustments");@+p->SIC=ADJ_SEC;@+node_index[ADJ_SEC]=p; strcpy((p+1)->title,"Users");@+node_index[MAX_N]=p+1; @ The remaining part of \.{econ.dat} is an $81\times80$ matrix in which the $k$th row contains the outputs of sector~$k$ to all sectors except \.{Users}. Each row consists of a blank line followed by 8 data lines; each data line contains 10 numbers separated by commas. Zeroes are represented by |""| instead of by |"0"|. For example, the data line $$\hbox{\tt 8490,2182,42,467,,,,,,}$$ follows the initial blank line; it means that sector~1 output 8490 million dollars to itself, \$2182M to sector~2, \dots, \$0M to sector~10. @<Read and store the output...@>= {@+register long s=0; /* row sum */ register long x; /* entry read from \.{econ.dat} */ if (gb_char()!='\n') panic(syntax_error+5); /* blank line missing between rows */ gb_newline(); p=node_index[k]; for (j=1;j<MAX_N;j++) { p->table[j]=x=gb_number(10);@+s+=x; node_index[j]->total+=x; if ((j%10)==0) { if (gb_char()!='\n') panic(syntax_error+6); /* out of synch in input file */ gb_newline(); }@+else if (gb_char()!=',') panic(syntax_error+7); /* missing comma after entry */ } p->table[MAX_N]=s; /* sum of |table[1]| through |table[80]| */ } @* Growing a subtree. Once all the data appears in |node_block|, we want to extract from it and combine~it as specified by parameters |n|, |omit|, and |seed|. This amalgamation process effectively prunes the tree; it can also be regarded as a procedure that grows a subtree of the full economic tree. @<Determine the |n| sectors to use in the graph@>= {@+long l=n+omit-2; /* the number of leaves in the desired subtree */ if (l==NORM_N) @<Choose all sectors@>@; else if (seed) @<Grow a random subtree with |l| leaves@>@; else @<Grow a subtree with |l| leaves by subdividing largest sectors first@>; } @ The chosen leaves of our subtree are identified by having their |tag| field set to~1. @<Choose all sectors@>= for (k=NORM_N;k;k--) node_index[k]->tag=1; @ To grow the |l|-leaf subtree when |seed=0|, we first pass over the tree bottom-up to compute the total input (and output) of each macro-sector; then we proceed from the top down to subdivide sectors in decreasing order of their total input. This process provides a good introduction to the bottom-up and top-down tree methods we will be using in several other parts of the program. The |special| node is used here for two purposes: It is the head of a linked list of unexplored nodes, sorted by decreasing order of their |total| fields; and it appears at the end of that list, because |special->total=0|. @<Grow a subtree with |l| leaves by subdividing largest sectors first@>= {@+register node *special=node_index[MAX_N]; /* the \.{Users} node at the end of |node_block| */ for (p=node_index[ADJ_SEC]-1;p>=node_block;p--) /* bottom up */ if (p->rchild) p->total=(p+1)->total+p->rchild->total; special->link=node_block;@+node_block->link=special; /* start at the root */ k=1; /* |k| is the number of nodes we have tagged or put onto the list */ while (k<l) @<If the first node on the list is a leaf, delete it and tag it; otherwise replace it by its two children@>; for (p=special->link;p!=special;p=p->link) p->tag=1; /* tag everything on the list */ } @ @<If the first node on the list is a leaf,...@>= { p=special->link; /* remove |p|, the node with greatest |total| */ special->link=p->link; if (p->rchild==0) p->tag=1; /* |p| is a leaf */ else { pl=p+1;@+pr=p->rchild; for (q=special;q->link->total>pl->total;q=q->link) ; pl->link=q->link;@+q->link=pl; /* insert left child in its proper place */ for (q=special;q->link->total>pr->total;q=q->link) ; pr->link=q->link;@+q->link=pr; /* insert right child in its proper place */ k++; } } @ We can obtain a uniformly distributed |l|-leaf subtree of a given tree by choosing the root when |l=1| or by using the following idea when |l>1|: Suppose the given tree~$T$ has subtrees $T_0$ and $T_1$. Then it has $T(l)$ subtrees with |l|~leaves, where $T(l)=\sum_k T_0(k)T_1(l-k)$. We choose a random number $r$ between 0 and $T(l)-1$, and we find the smallest $m$ such that $\sum_{k\le m}T_0(k)T_1(l-k)>r$. Then we proceed recursively to compute a random $m$-leaf subtree of~$T_0$ and a random $(l-m)$-leaf subtree of~$T_1$. A difficulty arises when $T(l)$ is $2^{31}$ or more. But then we can replace $T_0(k)$ and $T_1(l-k)$ in the formulas above by $\lceil T_0(k)/d_0\rceil$ and $\lceil T_1(k)/d_1\rceil$, respectively, where $d_0$ and $d_1$ are arbitrary constants; this yields smaller values $T(l)$ that define approximately the same distribution of~$k$. The program here computes the $T(l)$ values bottom-up, then grows a random tree top-down. If node~|p| is not a leaf, its |table[0]| field will be set to the number of leaves below it; and its |table[l]| field will be set to $T(l)$, for |1<=l<=table[0]|. The data in \.{econ.dat} is sufficiently simple that most of the $T(l)$ values are less than $2^{31}$. We need to scale them down to avoid overflow only at the root node of the tree; this case is handled separately. We set the |tag| field of a node equal to the number of leaves to be grown in the subtree rooted at that node. This convention is consistent with our previous stipulation that |tag=1| should characterize the nodes that are chosen to be vertices. @<Grow a random subtree with |l| leaves@>= { node_block->tag=l; for (p=node_index[ADJ_SEC]-1;p>node_block;p--) /* bottom up, except root */ if (p->rchild) @<Compute the $T(l)$ values for subtree |p|@>; for (p=node_block;p<node_index[ADJ_SEC];p++) /* top down, from root */ if (p->tag>1) { l=p->tag; pl=p+1;@+pr=p->rchild; if (pl->rchild==NULL) { pl->tag=1;@+pr->tag=l-1; }@+else if (pr->rchild==NULL) { pl->tag=l-1;@+pr->tag=1; }@+else @<Stochastically determine the number of leaves to grow in each of |p|'s children@>; } } @ Here we are essentially multiplying two generating functions. Suppose $f(z)=\sum_l T(l)z^l$; then we are computing $f_p(z)= z+f_{pl}(z)f_{pr}(z)$. @<Compute the $T(l)$ values for subtree |p|@>= { pl=p+1;@+pr=p->rchild; p->table[1]=p->table[2]=1; /* $T(1)$ and $T(2)$ are always 1 */ if (pl->rchild==0) { /* left child is a leaf */ if (pr->rchild==0) p->table[0]=2; /* and so is the right child */ else { /* no, it isn't */ for (k=2;k<=pr->table[0];k++) p->table[1+k]=pr->table[k]; p->table[0]=pr->table[0]+1; } }@+else if (pr->rchild==0) { /* right child is a leaf */ for (k=2;k<=pl->table[0];k++) p->table[1+k]=pl->table[k]; p->table[0]=pl->table[0]+1; }@+else { /* neither child is a leaf */ @<Set |p->table[2]|, |p->table[3]|, \dots\ to convolution of |pl| and |pr| table entries@>; p->table[0]=pl->table[0]+pr->table[0]; } } @ @<Set |p->table[2]|, |p->table[3]|, \dots\ to convolution...@>= p->table[2]=0; for (j=pl->table[0];j;j--) {@+register long t=pl->table[j]; for (k=pr->table[0];k;k--) p->table[j+k]+=t*pr->table[k]; } @ @<Stochastically determine the number of leaves to grow...@>= {@+register long ss,rr; j=0; /* we will set |j=1| if scaling is necessary at the root */ if (p==node_block) { ss=0; if (l>29 && l<67) { j=1; /* more than $2^{31}$ possibilities exist */ for (k=(l>pr->table[0]? l-pr->table[0]: 1);k<=pl->table[0] && k<l;k++) ss+=((pl->table[k]+0x3ff)>>10)*pr->table[l-k]; /* scale with $d_0=1024$, $d_1=1$ */ }@+else for (k=(l>pr->table[0]? l-pr->table[0]: 1);k<=pl->table[0] && k<l;k++) ss+=pl->table[k]*pr->table[l-k]; }@+else ss=p->table[l]; rr=gb_unif_rand(ss); if (j) for (ss=0,k=(l>pr->table[0]? l-pr->table[0]: 1);ss<=rr;k++) ss+=((pl->table[k]+0x3ff)>>10)*pr->table[l-k]; else for (ss=0,k=(l>pr->table[0]? l-pr->table[0]: 1);ss<=rr;k++) ss+=pl->table[k]*pr->table[l-k]; pl->tag=k-1;@+pr->tag=l-k+1; } @* Arcs. In the general case, we have to combine some of the basic micro-sectors into macro-sectors by adding together the appropriate input/output coefficients. This is a bottom-up pruning process. Suppose |p| is being formed as the union of |pl| and~|pr|. Then the arcs leading out of |p| are obtained by summing the numbers on arcs leading out of |pl| and~|pr|; the arcs leading into |p| are obtained by summing the numbers on arcs leading into |pl| and~|pr|; the arcs from |p| to itself are obtained by summing the four numbers on arcs leading from |pl| or~|pr| to |pl| or~|pr|. We maintain the |node_index| table so that its non-|NULL| entries contain all the currently active nodes. When |pl| and~|pr| are being pruned in favor of~|p|, node |p|~inherits |pl|'s place in |node_index|; |pr|'s former place becomes~|NULL|. @<Put the appropriate arcs into the graph@>= @<Prune the sectors that are used in macro-sectors, and form the lists of SIC sector codes@>; @<Make the special nodes invisible if they are omitted, visible otherwise@>; @<Compute individual thresholds for each chosen sector@>; {@+register Vertex *v=new_graph->vertices+n; for (k=MAX_N;k;k--) if ((p=node_index[k])!=NULL) { vert_index[k]=--v; v->name=gb_save_string(p->title); v->SIC_codes=p->SIC_list; v->sector_total=p->total; }@+else vert_index[k]=NULL; if (v!=new_graph->vertices) panic(impossible); /* bug in algorithm; this can't happen */ for (j=MAX_N;j;j--) if ((p=node_index[j])!=NULL) {@+register Vertex *u=vert_index[j]; for (k=MAX_N;k;k--) if ((v=vert_index[k])!=NULL) if (p->table[k]!=0 && p->table[k]>node_index[k]->thresh) { gb_new_arc(u,v,1L); u->arcs->flow=p->table[k]; } } } @ @<Private v...@>= static Vertex *vert_index[MAX_N+1]; /* the vertex assigned to an SIC code */ @ The theory underlying this step is the following, for integers $a,b,c,d$ with $b,d>0$: $$ {a\over b}>{c\over d} \qquad\iff\qquad a>\biggl\lfloor{b\over d}\biggr\rfloor\,c + \biggl\lfloor{(b\bmod d)c\over d}\biggr\rfloor\,.$$ In our case, $b=\hbox{|p->total|}$ and $c=threshold\le d=65536=2^{16}$, hence the multiplications cannot overflow. (But they can come awfully darn close.) @<Compute individual thresholds for each chosen sector@>= for (k=MAX_N;k;k--) if ((p=node_index[k])!=NULL) { if (threshold==0) p->thresh=-99999999; else p->thresh=((p->total>>16)*threshold)+ (((p->total&0xffff)*threshold)>>16); } @ @<Prune the sectors that are used in macro-sectors, and form the lists of SIC sector codes@>= for (p=node_index[ADJ_SEC];p>=node_block;p--) { /* bottom up */ if (p->SIC) { /* original leaf */ p->SIC_list=gb_virgin_arc(); p->SIC_list->len=p->SIC; }@+else { pl=p+1;@+pr=p->rchild; if (p->tag==0) p->tag=pl->tag+pr->tag; if (p->tag<=1) @<Replace |pl| and |pr| by their union, |p|@>; } } @ @<Replace |pl| and |pr| by their union, |p|@>= {@+register Arc *a=pl->SIC_list; register long jj=pl->SIC, kk=pr->SIC; p->SIC_list=a; while (a->next) a=a->next; a->next=pr->SIC_list; for (k=MAX_N;k;k--) if ((q=node_index[k])!=NULL) { if (q!=pl && q!=pr) q->table[jj]+=q->table[kk]; p->table[k]=pl->table[k]+pr->table[k]; } p->total=pl->total+pr->total; p->SIC=jj; p->table[jj]+=p->table[kk]; node_index[jj]=p; node_index[kk]=NULL; } @ If the \.{Users} vertex is not omitted, we need to compute each sector's total final demand, which is calculated so that the row sums and column sums of the input/output coefficients come out equal. We've already computed the column sum, |p->total|; we've also computed |p->table[1]+@[@t\hbox{$\cdots$}@>@]+p->table[ADJ_SEC]|, and put it into |p->table[MAX_N]|. So now we want to replace |p->table[MAX_N]| by |p->total-p->table[MAX_N]|. As remarked earlier, this quantity might be negative. In the special node |p| for the \.{Users} vertex, the preliminary processing has made |p->total=0|; moreover, |p->table[MAX_N]| is the sum of value added, or GNP. We want to switch those fields. We don't have to set the |tag| fields to 1 in the special nodes, because the remaining parts of the arc-generation algorithm don't look at those fields. @<Make the special nodes invisible if they are omitted, visible otherwise@>= if (omit==2) node_index[ADJ_SEC]=node_index[MAX_N]=NULL; else if (omit==1) node_index[MAX_N]=NULL; else { for (k=ADJ_SEC;k;k--) if ((p=node_index[k])!=NULL) p->table[MAX_N]=p->total-p->table[MAX_N]; p=node_index[MAX_N]; /* the special node */ p->total=p->table[MAX_N]; p->table[MAX_N]=0; } @* Index. As usual, we close with an index that shows where the identifiers of \\{gb\_econ} are defined and used. ���������������gb_games.w������������������������������������������������������������������������������������������0000444�0001750�0001750�00000046240�06430504340�011136� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_\,GAMES} \prerequisites{GB\_\,GRAPH}{GB\_\,IO} @* Introduction. This GraphBase module contains the |games| subroutine, which creates a family of undirected graphs based on college football scores. An example of the use of this procedure can be found in the demo program {\sc FOOTBALL}. @(gb_games.h@>= extern Graph *games(); @ The subroutine call |games|(|n|, |ap0_weight|, |upi0_weight|, |ap1_weight|, |upi1_weight|, |first_day|, |last_day|, |seed|) constructs a graph based on the information in \.{games.dat}. Each vertex of the graph corresponds to one of 120 football teams at American colleges and universities (more precisely, to the 106 college football teams of division I-A together with the 14 division I-AA teams of the Ivy League and the Patriot League). Each edge of the graph corresponds to one of the 638 games played between those teams during the 1990 season. An arc from vertex~|u| to vertex~|v| is assigned a length representing the number of points scored by |u| when playing~|v|. Thus the graph isn't really ``undirected,'' although it is true that its arcs are paired (i.e., that |u| played~|v| if and only if |v| played~|u|). A truly undirected graph with the same vertices and edges can be obtained by applying the |complement| routine of {\sc GB\_\,BASIC}. The constructed graph will have $\min(n,120)$ vertices. If |n| is less than 120, the |n| teams will be selected by assigning a weight to each team and choosing the |n| with largest weight, using random numbers to break ties in case of equal weights. Weights are computed by the formula $$ |ap0_weight|\cdot|ap0|+|upi0_weight|\cdot|upi0| +|ap1_weight|\cdot|ap1|+|upi1_weight|\cdot|upi1|, $$ where |ap0| and |upi0| are the point scores given to a team in the Associated Press and United Press International polls at the beginning of the season, and |ap1| and |upi1| are the similar scores given at the end of the season. (The \\{ap} scores were obtained by asking 60 sportswriters to choose and rank the top 25 teams, assigning 25 points to a team ranked 1st and 1 point to a team ranked 25th; thus the total of each of the \\{ap} scores, summed over all teams, is 19500. The \\{upi} scores were obtained by asking football coaches to choose and rank the top 15 teams, assigning 15 points to a team ranked 1st and 1 point to a team ranked 15th. In the case of \\{upi0}, there were 48 coaches voting, making 5760 points altogether; but in the case of \\{upi1}, 59 coaches were polled, yielding a total of 7080 points. The coaches agreed not to vote for any team that was on probation for violating NCAA rules, but the sportswriters had no such policy.) Parameters |first_day| and |last_day| can be used to vary the number of edges; only games played between |first_day| and |last_day|, inclusive, will be included in the constructed graph. Day~0 was August~26, 1990, when Colorado and Tennessee competed in the Disneyland Pigskin Classic. Day~128 was January~1, 1991, when the final end-of-season bowl games were played. About half of each team's games were played between day~0 and day~50. If |last_day=0|, the value of |last_day| is automatically increased to~128. As usual in GraphBase routines, you can set |n=0| to get the default situation where |n| has its maximum value. For example, either |games(0,0,0,0,0,0,0,0)| or |games(120,0,0,0,0,0,0,0)| produces the full graph; |games(0,0,0,0,0,50,0,0)| or |games(120,0,0,0,0,50,0,0)| or |games(120,0,0,0,0,50,128,0)| produces the graph for the last half of the season. One way to select a subgraph containing the 30 ``best'' teams is to ask for |games(30,0,0,1,2,0,0,0)|, which adds the votes of the sportswriters to the votes of the coaches (considering that a coach's first choice is worth 30 points while a sportswriter's first choice is worth only 25). It turns out that 67 of the teams did not receive votes in any of the four polls; the subroutine call |games(53,1,1,1,1,0,0,0)| will pick out the 53 teams that were selected at least once by some sportswriter or coach, and |games(67,-1,-1,-1,-1,0,0,0)| will pick out the 67 that were not. A~random selection of 60 teams can be obtained by calling |games(60,0,0,0,0,0,0,s)|. Different choices of the seed number~|s| will produce different selections in a system-independent manner; any value of |s| between 0 and $2^{31}-1$ is permissible. If you ask for |games(120,0,0,0,0,0,0,s)| with different choices of~|s|, you always get the full graph, but the vertices will appear in different (random) orderings depending on~|s|. Parameters |ap0_weight|, |upi0_weight|, |ap1_weight|, and |upi1_weight| must be at most $2^{17}=131072$ in absolute value. @d MAX_N 120 @d MAX_DAY 128 @d MAX_WEIGHT 131072 @d ap u.I /* Associated Press scores: |(ap0<<16)+ap1| */ @d upi v.I /* United Press International scores |(upi0<<16)+upi1| */ @ Most of the teams belong to a ``conference,'' and they play against almost every other team that belongs to the same conference. For example, Stanford and nine other teams belong to the Pacific Ten conference. Eight of Stanford's eleven games were against other teams of the Pacific Ten; the other three were played against Colorado (from the Big Eight), San Jos\'e State (from the Big West) and Notre Dame (which is independent). The graphs produced by |games| therefore illustrate ``cliquey'' patterns of social interaction. Eleven different conferences are included in \.{games.dat}. Utility field |z.S| of a vertex is set to the name of a team's conference, or to |NULL| if that team is independent. (Exactly 24 of the I-A football teams were independent in 1990.) Two teams |u| and |v| belong to the same conference if and only if |u->conference==v->conference| and |u->conference!=NULL|. @d conference z.S @ Each team has a nickname, which is recorded in utility field |y.S|. For example, Georgia Tech's team is called the Yellow Jackets. Six teams (Auburn, Clemson, Memphis State, Missouri, Pacific, and Princeton) are called the Tigers, and five teams (Fresno State, Georgia, Louisiana Tech, Mississippi State, Yale) are called the Bulldogs. But most of the teams have a unique nickname, and 94 distinct nicknames exist. A shorthand code for team names is also provided, in the |abbr| field. @d nickname y.S @d abbr x.S @ If |a| points to an arc from |u| to |v|, utility field |a->a.I| contains the value 3 if |u| was the home team, 1 if |v| was the home team, and 2 if both teams played on neutral territory. The date of that game, represented as a integer number of days after August~26, 1990, appears in utility field |a->b.I|. The arcs in each vertex list |v->arcs| appear in reverse order of their dates: last game first and first game last. @d HOME 1 @d NEUTRAL 2 /* this value is halfway between |HOME| and |AWAY| */ @d AWAY 3 @d venue a.I @d date b.I @(gb_games.h@>= #define ap @[u.I@] /* repeat the definitions in the header file */ #define upi @[v.I@] #define abbr @[x.S@] #define nickname @[y.S@] #define conference @[z.S@] #define HOME 1 #define NEUTRAL 2 #define AWAY 3 #define venue @[a.I@] #define date @[b.I@] @ If the |games| routine encounters a problem, it returns |NULL| (\.{NULL}), after putting a code number into the external variable |panic_code|. This code number identifies the type of failure. Otherwise |games| 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_trouble_code=0;@+return NULL;@+} @ The \CEE/ file \.{gb\_games.c} has the following overall shape: @p #include "gb_io.h" /* we will use the {\sc GB\_\,IO} routines for input */ #include "gb_flip.h" /* we will use the {\sc GB\_\,FLIP} routines for random numbers */ #include "gb_graph.h" /* we will use the {\sc GB\_\,GRAPH} data structures */ #include "gb_sort.h" /* and |gb_linksort| for sorting */ @h@# @<Type declarations@>@; @<Private variables@>@; @<Private functions@>@; @# Graph *games(n,ap0_weight,upi0_weight,ap1_weight,upi1_weight, first_day,last_day,seed) unsigned long n; /* number of vertices desired */ long ap0_weight; /* coefficient of |ap0| in the weight function */ long ap1_weight; /* coefficient of |ap1| in the weight function */ long upi0_weight; /* coefficient of |upi0| in the weight function */ long upi1_weight; /* coefficient of |upi1| in the weight function */ long first_day; /* lower cutoff for games to be considered */ long last_day; /* upper cutoff for games to be considered */ long seed; /* random number seed */ {@+@<Local variables@>@;@# gb_init_rand(seed); @<Check that the parameters are valid@>; @<Set up a graph with |n| vertices@>; @<Read the first part of \.{games.dat} and compute team weights@>; @<Determine the |n| teams to use in the graph@>; @<Put the appropriate edges into the graph@>; if (gb_close()!=0) panic(late_data_fault); /* something's wrong with |"games.dat"|; see |io_errors| */ gb_free(working_storage); if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } return new_graph; } @ @<Local var...@>= Graph *new_graph; /* the graph constructed by |games| */ register long j,k; /* all-purpose indices */ @ @<Check that the parameters are valid@>= if (n==0 || n>MAX_N) n=MAX_N; if (ap0_weight>MAX_WEIGHT || ap0_weight<-MAX_WEIGHT || upi0_weight>MAX_WEIGHT || upi0_weight<-MAX_WEIGHT ||@| ap1_weight>MAX_WEIGHT || ap1_weight<-MAX_WEIGHT || upi1_weight>MAX_WEIGHT || upi1_weight<-MAX_WEIGHT) panic(bad_specs); /* the magnitude of at least one weight is too big */ if (first_day<0) first_day=0; if (last_day==0 || last_day>MAX_DAY) last_day=MAX_DAY; @ @<Set up a graph with |n| vertices@>= 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,"games(%lu,%ld,%ld,%ld,%ld,%ld,%ld,%ld)", n,ap0_weight,upi0_weight,ap1_weight,upi1_weight,first_day,last_day,seed); strcpy(new_graph->util_types,"IIZSSSIIZZZZZZ"); @* Vertices. As we read in the data, we construct a list of nodes, each of which contains a team's name, nickname, conference, and weight. After this list has been sorted by weight, the top |n| entries will be the vertices of the new graph. @<Type decl...@>= typedef struct node_struct { /* records to be sorted by |gb_linksort| */ long key; /* the nonnegative sort key (weight plus $2^{30}$) */ struct node_struct *link; /* pointer to next record */ char name[24]; /* |"College Name"| */ char nick[22]; /* |"Team Nickname"| */ char abb[6]; /* |"ABBR"| */ long a0,u0,a1,u1; /* team scores in press polls */ char *conf; /* pointer to conference name */ struct node_struct *hash_link; /* pointer to next \.{ABBR} in hash list */ Vertex *vert; /* vertex corresponding to this team */ } node; @ The data in \.{games.dat} appears in two parts. The first 120 lines have the form $$\hbox{\tt ABBR College Name(Team Nickname)Conference;a0,u0;a1,u1}$$ and they give basic information about the teams. An internal abbreviation code \.{ABBR} is used to identify each team in the second part of the data. The second part presents scores of the games, and it contains two kinds of lines. If the first character of a line is `\.>', it means ``change the current date,'' and the remaining characters specify a date as a one-letter month code followed by the day of the month. Otherwise the line gives scores of a game, using the \.{ABBR} codes for two teams. The scores are separated by `\.@@' if the second team was the home team and by `\.,' if both teams were on neutral territory. For example, two games were played on December 8, namely the annual Army-Navy game and the California Raisin Bowl game. These are recorded in three lines of \.{games.dat} as follows: $$\vbox{\halign{\tt#\hfil\cr >D8\cr NAVY20@@ARMY30\cr SJSU48,CMICH24\cr}}$$ We deduce that Navy played at Army's home stadium, losing 20 to~30; moreover, San Jos\'e State played Central Michigan on neutral territory and won, 48 to~24. (The California Raisin Bowl is traditionally a playoff between the champions of the Big West and Mid-American conferences.) @ In order to map \.{ABBR} codes to team names, we use a simple hash coding scheme. Two abbreviations with the same hash address are linked together via the |hash_link| address in their node. The constants defined here are taken from the specific data in \.{games.dat}, because this routine is not intended to be perfectly general. @d HASH_PRIME 1009 @<Private v...@>= static long ma0=1451,mu0=666,ma1=1475,mu1=847; /* maximum poll values in the data */ static node *node_block; /* array of nodes holding team info */ static node **hash_block; /* array of heads of hash code lists */ static Area working_storage; /* memory needed only while |games| is working */ static char **conf_block; /* array of conference names */ static long m; /* the number of conference names known so far */ @ @<Read the first part of \.{games.dat} and compute team weights@>= node_block=gb_typed_alloc(MAX_N+2,node,working_storage); /* leave room for string overflow */ hash_block=gb_typed_alloc(HASH_PRIME,node*,working_storage); conf_block=gb_typed_alloc(MAX_N,char*,working_storage); m=0; if (gb_trouble_code) { gb_free(working_storage); panic(no_room+1); /* nowhere to copy the data */ } if (gb_open("games.dat")!=0) panic(early_data_fault); /* couldn't open |"games.dat"| using GraphBase conventions; |io_errors| tells why */ for (k=0; k<MAX_N; k++) @<Read and store data for team |k|@>; @ @<Read and store...@>= {@+register node *p; register char *q; p=node_block+k; if (k) p->link=p-1; q=gb_string(p->abb,' '); if (q>&p->abb[6] || gb_char()!=' ') panic(syntax_error); /* out of sync in \.{games.dat} */ @<Enter |p->abb| in the hash table@>; q=gb_string(p->name,'('); if (q>&p->name[24] || gb_char()!='(') panic(syntax_error+1); /* team name too long */ q=gb_string(p->nick,')'); if (q>&p->nick[22] || gb_char()!=')') panic(syntax_error+2); /* team nickname too long */ @<Read the conference name for |p|@>; @<Read the press poll scores for |p| and compute |p->key|@>; gb_newline(); } @ @<Enter |p->abb| in the hash table@>= {@+long h=0; /* the hash code */ for (q=p->abb;*q;q++) h=(h+h+*q)%HASH_PRIME; p->hash_link=hash_block[h]; hash_block[h]=p; } @ @<Read the conference name for |p|@>= { gb_string(str_buf,';'); if (gb_char()!=';') panic(syntax_error+3); /* conference name clobbered */ if (strcmp(str_buf,"Independent")!=0) { for (j=0;j<m;j++) if (strcmp(str_buf,conf_block[j])==0) goto found; conf_block[m++]=gb_save_string(str_buf); found:p->conf=conf_block[j]; } } @ The key value computed here will be between 0 and~$2^{31}$, because of the bound we've imposed on the weight parameters. @<Read the press poll scores for |p| and compute |p->key|@>= p->a0=gb_number(10); if (p->a0>ma0 || gb_char()!=',') panic(syntax_error+4); /* first AP score clobbered */ p->u0=gb_number(10); if (p->u0>mu0 || gb_char()!=';') panic(syntax_error+5); /* first UPI score clobbered */ p->a1=gb_number(10); if (p->a1>ma1 || gb_char()!=',') panic(syntax_error+6); /* second AP score clobbered */ p->u1=gb_number(10); if (p->u1>mu1 || gb_char()!='\n') panic(syntax_error+7); /* second UPI score clobbered */ p->key=ap0_weight*(p->a0)+upi0_weight*(p->u0) +ap1_weight*(p->a1)+upi1_weight*(p->u1)+0x40000000; @ Once all the nodes have been set up, we can use the |gb_linksort| routine to sort them into the desired order. It builds 128 lists from which the desired nodes are readily accessed in decreasing order of weight, using random numbers to break ties. We set the abbreviation code to zero in every team that isn't chosen. Then games involving that team will be excluded when edges are generated below. @<Determine the |n| teams to use in the graph@>= {@+register node *p; /* the current node being considered */ register Vertex *v=new_graph->vertices; /* the next vertex to use */ gb_linksort(node_block+MAX_N-1); for (j=127; j>=0; j--) for (p=(node*)gb_sorted[j]; p; p=p->link) { if (v<new_graph->vertices+n) @<Add team |p| to the graph@>@; else p->abb[0]='\0'; /* this team is not being used */ } } @ @<Add team |p| to the graph@>= { v->ap=((long)(p->a0)<<16)+p->a1; v->upi=((long)(p->u0)<<16)+p->u1; v->abbr=gb_save_string(p->abb); v->nickname=gb_save_string(p->nick); v->conference=p->conf; v->name=gb_save_string(p->name); p->vert=v++; } @* Arcs. Finally, we read through the rest of \.{games.dat}, adding a pair of arcs for each game that belongs to the selected time interval and was played by two of the selected teams. @<Put the appropriate edges into the graph@>= {@+register Vertex *u,*v; register long today=0; /* current day of play */ long su,sv; /* points scored by each team */ long ven; /* |HOME| if |v| is home team, |NEUTRAL| if on neutral ground */ while (!gb_eof()) { if (gb_char()=='>') @<Change the current date@>@; else gb_backup(); u=team_lookup(); su=gb_number(10); ven=gb_char(); if (ven=='@@') ven=HOME; else if (ven==',') ven=NEUTRAL; else panic(syntax_error+8); /* bad syntax in game score line */ v=team_lookup(); sv=gb_number(10); if (gb_char()!='\n') panic(syntax_error+9); /* bad syntax in game score line */ if (u!=NULL && v!=NULL && today>=first_day && today<=last_day) @<Enter a new edge@>; gb_newline(); } } @ @<Change the current...@>= {@+register char c=gb_char(); /* month code */ register long d; /* day of football season */ switch(c) { case 'A': d=-26;@+break; /* August */ case 'S': d=5;@+break; /* thirty days hath September */ case 'O': d=35;@+break; /* October */ case 'N': d=66;@+break; /* November */ case 'D': d=96;@+break; /* December */ case 'J': d=127;@+break; /* January */ default: d=1000; } d+=gb_number(10); if (d<0 || d>MAX_DAY) panic(syntax_error-1); /* date was clobbered */ today=d; gb_newline(); /* now ready to read a non-date line */ } @ @<Private f...@>= static Vertex *team_lookup() /* read and decode an abbreviation */ {@+register char *q=str_buf; /* position in |str_buf| */ register long h=0; /* hash code */ register node *p; /* position in hash list */ while (gb_digit(10)<0) { *q=gb_char(); h=(h+h+*q)%HASH_PRIME; q++; } gb_backup(); /* prepare to re-scan the digit following the abbreviation */ *q='\0'; /* null-terminate the abbreviation just scanned */ for (p=hash_block[h];p;p=p->hash_link) if (strcmp(p->abb,str_buf)==0) return p->vert; return NULL; /* not found */ } @ We retain the convention of {\sc GB\_\,GRAPH} that the arc from |v| to |u| appears immediately after a matching arc from |u| to |v| when |u<v|. @<Enter a new edge@>= {@+register Arc *a; if (u>v) {@+register Vertex *w; register long sw; w=u;@+u=v;@+v=w; sw=su;@+su=sv;@+sv=sw; ven=HOME+AWAY-ven; } gb_new_arc(u,v,su); gb_new_arc(v,u,sv); a=u->arcs; /* a pointer to the new arc */ if (v->arcs!=a+1) panic (impossible+9); /* can't happen */ a->venue=ven;@+(a+1)->venue=HOME+AWAY-ven; a->date=(a+1)->date=today; } @* Index. As usual, we close with an index that shows where the identifiers of \\{gb\_games} are defined and used. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gb_gates.w������������������������������������������������������������������������������������������0000444�0001750�0001750�00000225204�11077542620�011152� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_\,GATES} \prerequisite{GB\_\,GRAPH} @* Introduction. This GraphBase module provides six external subroutines: $$\vbox{\hsize=.8\hsize \everypar{\hangindent3em} \noindent|risc|, a routine that creates a directed acyclic graph based on the logic of a simple RISC computer;\par \noindent|prod|, a routine that creates a directed acyclic graph based on the logic of parallel multiplication circuits;\par \noindent|print_gates|, a routine that outputs a symbolic representation of such directed acyclic graphs;\par \noindent|gate_eval|, a routine that evaluates such directed acyclic graphs by assigning boolean values to each gate;\par \noindent|partial_gates|, a routine that extracts a subgraph by assigning random values to some of the input gates;\par \noindent|run_risc|, a routine that can be used to play with the output of |risc|.}$$ Examples of the use of these routines can be found in the demo programs {\sc TAKE\_\,RISC} and {\sc MULTIPLY}. @(gb_gates.h@>= #define print_gates p_gates /* abbreviation for Procrustean linkers */ extern Graph *risc(); /* make a network for a microprocessor */ extern Graph *prod(); /* make a network for high-speed multiplication */ extern void print_gates(); /* write a network to standard output file */ extern long gate_eval(); /* evaluate a network */ extern Graph *partial_gates(); /* reduce network size */ extern long run_risc(); /* simulate the microprocessor */ extern unsigned long risc_state[]; /* the output of |run_risc| */ @ The directed acyclic graphs produced by {\sc GB\_\,GATES} are GraphBase graphs with special conventions related to logical networks. Each vertex represents a gate of a network, and utility field |val| is a boolean value associated with that gate. Utility field |typ| is an ASCII code that tells what kind of gate is present: {\advance\parindent 2em \smallskip \item{|'I'|} denotes an input gate, whose value is specified externally. \smallskip \item{|'&'|} denotes an \.{AND} gate, whose value is the logical {\sc AND} of two or more previous gates (namely, 1 if all those gates are~1, otherwise~0). \smallskip \item{|'|'|} denotes an \.{OR} gate, whose value is the logical {\sc OR} of two or more previous gates (namely, 0 if all those gates are~0, otherwise~1). \smallskip \item{|'^'|} denotes an \.{XOR} gate, whose value is the logical {\sc EXCLUSIVE-OR} of two or more previous gates (namely, their sum modulo~2). \smallskip \item{|'~'|} denotes an inverter, whose value is the logical complement of the value of a single previous gate. \smallskip \item{|'L'|} denotes a latch, whose value depends on past history; it is the value that was assigned to a subsequent gate when the network was most recently evaluated. Utility field |alt| points to that subsequent gate. \smallskip}\noindent Latches can be used to include ``state'' information in a circuit; for example, they correspond to registers of the RISC machine constructed by |risc|. The |prod| procedure does not use latches. The vertices of the directed acyclic graph appear in a special ``topological'' order convenient for evaluation: All the input gates come first, followed by all the latches; then come the other types of gates, whose values are computed from their predecessors. The arcs of the graph run from each gate to its arguments, and all arguments to a gate precede that gate. If |g| points to such a graph of gates, the utility field |g->outs| points to a list of |Arc| records, denoting ``outputs'' that might be used in certain applications. For example, the outputs of the graphs created by |prod| correspond to the bits of the product of the numbers represented in the input gates. A special convention is used so that the routines will support partial evaluation: The |tip| fields in the output list either point to a vertex or hold one of the constant values 0 or~1 when regarded as an unsigned long integer. @d val x.I /* the field containing a boolean value */ @d typ y.I /* the field containing the gate type */ @d alt z.V /* the field pointing to another related gate */ @d outs zz.A /* the field pointing to the list of output gates */ @d is_boolean(v) ((unsigned long)(v)<=1) /* is a |tip| field constant? */ @d the_boolean(v) ((long)(v)) /* if so, this is its value */ @d tip_value(v) (is_boolean(v)? the_boolean(v): (v)->val) @d AND '&' @d OR '|' @d NOT '~' @d XOR '^' @(gb_gates.h@>= #define val @t\quad@> x.I /* the definitions are repeated in the header file */ #define typ @t\quad@> y.I #define alt @t\quad@> z.V #define outs @t\quad@> zz.A #define is_boolean(v) @t\quad@> ((unsigned long)(v)<=1) #define the_boolean(v) @t\quad@> ((long)(v)) #define tip_value(v) @t\quad@> (is_boolean(v)? the_boolean(v): (v)->val) #define AND @t\quad@> '&' #define OR @t\quad@> '|' #define NOT @t\quad@> '~' #define XOR @t\quad@> '^' @ Let's begin with the |gate_eval| procedure, because it is quite simple and because it illustrates the conventions just explained. Given a gate graph |g| and optional pointers |in_vec| and |out_vec|, the procedure |gate_eval| will assign values to each gate of~|g|. If |in_vec| is non-null, it should point to a string of characters, each |'0'| or~|'1'|, that will be assigned to the first gates of the network, in order; otherwise |gate_eval| assumes that all input gates have already received appropriate values and it will not change them. New values are computed for each gate after the bits of |in_vec| have been consumed. If |out_vec| is non-null, it should point to a memory area capable of receiving |m+1| characters, where |m| is the number of outputs of~|g|; a string containing the respective output values will be deposited there. If |gate_eval| encounters an unknown gate type, it terminates execution prematurely and returns the value |-1|. Otherwise it returns~0. @<The |gate_eval| routine@>= long gate_eval(g,in_vec,out_vec) Graph *g; /* graph with gates as vertices */ char *in_vec; /* string for input values, or |NULL| */ char *out_vec; /* string for output values, or |NULL| */ {@+register Vertex *v; /* the current vertex of interest */ register Arc *a; /* the current arc of interest */ register char t; /* boolean value being computed */ if (!g) return -2; /* no graph supplied! */ v=g->vertices; if (in_vec) @<Read a sequence of input values from |in_vec|@>; for (; v<g->vertices+g->n; v++) { switch (v->typ) { /* branch on type of gate */ case 'I': continue; /* this input gate's value should be externally set */ case 'L': t=v->alt->val;@+break; @t\4\4@>@<Compute the value |t| of a classical logic gate@>; default: return -1; /* unknown gate type! */ } v->val=t; /* assign the computed value */ } if (out_vec) @<Store the sequence of output values in |out_vec|@>; return 0; } @ @<Read a sequence...@>= while (*in_vec && v<g->vertices+g->n) (v++)->val = *in_vec++ - '0'; @ @<Store the sequence of output values in |out_vec|@>= { for (a=g->outs; a; a=a->next) *out_vec++='0'+tip_value(a->tip); *out_vec=0; /* terminate the string */ } @ @<Compute the value |t| of a classical logic gate@>= case AND: t=1; for (a=v->arcs; a; a=a->next) t &= a->tip->val; break; case OR: t=0; for (a=v->arcs; a; a=a->next) t |= a->tip->val; break; case XOR: t=0; for (a=v->arcs; a; a=a->next) t ^= a->tip->val; break; case NOT: t=1-v->arcs->tip->val; break; @ Here now is an outline of the entire {\sc GB\_\,GATES} module, as seen by the \CEE/ compiler: @p #include "gb_flip.h" /* we will use the {\sc GB\_\,FLIP} routines for random numbers */ #include "gb_graph.h" /* and we will use the {\sc GB\_\,GRAPH} data structures */ @h@# @<Private variables@>@; @<Global variables@>@; @<Internal subroutines@>@; @<The |gate_eval| routine@>@; @<The |print_gates| routine@>@; @<The |risc| routine@>@; @<The |run_risc| routine@>@; @<The |prod| routine@>@; @<The |partial_gates| routine@>@; @* The RISC netlist. The subroutine call |risc(regs)| creates a gate graph having |regs| registers; the value of |regs| must be between 2 and~16, inclusive, otherwise |regs| is set to~16. This gate graph describes the circuitry for a small RISC computer, defined below. The total number of gates turns out to be |1400+115*regs|; thus it lies between 1630 (when |regs=2|) and 3240 (when |regs=16|). {\sc EXCLUSIVE-OR} gates are not used; the effect of xoring is obtained where needed by means of {\sc AND}s, {\sc OR}s, and inverters. If |risc| cannot do its thing, it returns |NULL| (\.{NULL}) and sets |panic_code| to indicate the problem. Otherwise |risc| returns a pointer to the graph. @d panic(c) @+{@+panic_code=c;@+gb_trouble_code=0;@+return NULL;@+} @<The |risc| routine@>= Graph *risc(regs) unsigned long regs; /* number of registers supported */ {@+@<Local variables for |risc|@>@; @# @<Initialize |new_graph| to an empty graph of the appropriate size@>; @<Add the RISC data to |new_graph|@>; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } return new_graph; } @ @<Local variables for |risc|@>= Graph *new_graph; /* the graph constructed by |risc| */ register long k,r; /* all-purpose indices */ @ This RISC machine works with 16-bit registers and 16-bit data words. It cannot write into memory, but it assumes the existence of an external read-only memory. The circuit has 16 outputs, representing the 16 bits of a memory address register. It also has 17 inputs, the last 16 of which are supposed to be set to the contents of the memory address computed on the previous cycle. Thus we can run the machine by accessing memory between calls of |gate_eval|. The first input bit, called \.{RUN}, is normally set to~1; if it is~0, the other inputs are effectively ignored and all registers and outputs will be cleared to~0. Input bits for the memory appear in ``little-endian order,'' that is, least significant bit first; but the output bits for the memory address register appear in ``big-endian order,'' most significant bit first. Words read from memory are interpreted as instructions having the following format: $$\vbox{\offinterlineskip \def\\#1&{\omit&} \hrule \halign{&\vrule#&\strut\sevenrm\hbox to 1.7em{\hfil#\hfil}\cr height 5pt&\multispan7\hfill&&\multispan7\hfill&&\multispan3\hfill &&\multispan3\hfill&&\multispan7\hfill&\cr &\multispan7\hfill\.{DST}\hfill&&\multispan7\hfill\.{MOD}\hfill &&\multispan3\hfill\.{OP}\hfill&&\multispan3\hfill\.{A}\hfill &&\multispan7\hfill\.{SRC}\hfill&\cr height 5pt&\multispan7\hfill&&\multispan7\hfill&&\multispan3\hfill &&\multispan3\hfill&&\multispan7\hfill&\cr \noalign{\hrule} \\15&\\14&\\13&\\12&\\11&\\10&\\9&\\8&\\7&\\6&\\5&\\4&\\3&\\2&\\1&% \\0&\omit\cr}}$$ The \.{SRC} and \.A fields specify a ``source'' value. If $\.A=0$, the source is \.{SRC}, treated as a 4-bit signed number between $-8$ and $+7$ inclusive. If $\.A=1$, the source is the contents of register \.{DST} plus the (signed) value of \.{SRC}. If $\.A=2$, the source is the contents of register \.{SRC}. And if $\.A=3$, the source is the contents of the memory location whose address is the contents of register \.{SRC}. Thus, for example, if $\.{DST}=3$ and $\.{SRC}=10$, and if \.{r3} contains 17 while \.{r10} contains 1009, the source value will be $-6$ if $\.A=0$, or $17-6=11$ if $\.A=1$, or 1009 if $\.A=2$, or the contents of memory location 1009 if $\.A=3$. The \.{DST} field specifies the number of the destination register. This register receives a new value based on its previous value and the source value, as prescribed by the operation defined in the \.{OP} and \.{MOD} fields. For example, when $\.{OP}=0$, a general logical operation is performed, as follows: Suppose the bits of \.{MOD} are called $\mu_{11}\mu_{10}\mu_{01} \mu_{00}$ from left to right. Then if the $k$th bit of the destination register currently is equal to~$i$ and the $k$th bit of the source value is equal to~$j$, the general logical operator changes the $k$th bit of the destination register to~$\mu_{ij}$. If the \.{MOD} bits are, for example, $1010$, the source value is simply copied to the destination register; if $\.{MOD}=0110$, an exclusive-or is done; if $\.{MOD}=0011$, the destination register is complemented and the source value is effectively ignored. The machine contains four status bits called \.S (sign), \.N (nonzero), \.K (carry), and \.V (overflow). Every general logical operation sets \.S equal to the sign of the new result transferred to the destination register; this is bit~15, the most significant bit. A general logical operation also sets \.N to~1 if any of the other 15 bits are~1, to~0 if all of the other bits are~0. Thus \.S and \.N both become zero if and only if the new result is entirely zero. Logical operations do not change the values of \.K and~\.V; the latter are affected only by the arithmetic operations described below. The status of the \.S and \.N bits can be tested by using the conditional load operator, $\.{OP}=2$: This operation loads the source value into the destination register if and only if \.{MOD} bit $\mu_{ij}=1$, where $i$ and~$j$ are the current values of \.S and~\.N, respectively. For example, if $\.{MOD}=0011$, the source value is loaded if and only if $\.S=0$, which means that the last value affecting \.S and~\.N was greater than or equal to zero. If $\.{MOD}=1111$, loading is always done; this option provides a way to move source to destination without affecting \.S or~\.N. A second conditional load operator, $\.{OP}=3$, is similar, but it is used for testing the status of \.K and~\.V instead of \.S and~\.N. For example, a command having $\.{MOD}=1010$, $\.{OP}=3$, $\.A=1$, and $\.{SRC}=1$ adds the current overflow bit to the destination register. (Please take a moment to understand why this is true.) We have now described all the operations except those that are performed when $\.{OP}=1$. As you might expect, our machine is able to do rudimentary arithmetic. The general addition and subtraction operators belong to this final case, together with various shift operators, depending on the value of \.{MOD}. Eight of the $\.{OP}=1$ operations set the destination register to a shifted version of the source value: $\.{MOD}=0$ means ``shift left~1,'' which is equivalent to multiplying the source by~2; $\.{MOD}=1$ means ``cyclic shift left~1,'' which is the same except that it also adds the previous sign bit to the result; $\.{MOD}=2$ means ``shift left~4,'' which is equivalent to multiplying by~16; $\.{MOD}=3$ means ``cyclic shift left~4''; $\.{MOD}=4$ means ``shift right~1,'' which is equivalent to dividing the source by~2 and rounding down to the next lower integer if there was a remainder; $\.{MOD}=5$ means ``unsigned shift right~1,'' which is the same except that the most significant bit is always set to zero instead of retaining the previous sign; $\.{MOD}=6$ means ``shift right~4,'' which is equivalent to dividing the source by~16 and rounding down; $\.{MOD}=7$ means ``unsigned shift right~4.'' Each of these shift operations affects \.S and~\.N, as in the case of logical operations. They also affect \.K and~\.V, as follows: Shifting left sets \.K to~1 if and only if at least one of the bits shifted off the left was nonzero, and sets \.V to~1 if and only if the corresponding multiplication would cause overflow. Shifting right~1 sets \.K to the value of the bit shifted out, and sets \.V to~0; shifting right~4 sets \.K to the value of the last bit shifted out, and sets \.V to the logical {\sc OR} of the other three lost bits. The same values of \.K and \.V arise from cyclic or unsigned shifts as from ordinary shifts. When $\.{OP}=1$ and $\.{MOD}=8$, the source value is added to the destination register. This sets \.S, \.N, and \.V as you would expect; and it sets \.K to the carry you would get if you were treating the operands as 16-bit unsigned integers. Another addition operation, having $\.{MOD}=9$, is similar, but the current value of \.K is also added to the result; in this case, the new value of \.N will be zero if and only if the 15 non-sign bits of the result are zero and the previous values of \.S and~\.N were also zero. This means that you can use the first addition operation on the lower halves of a 32-bit number and the second operation on the upper halves, thereby obtaining a correct 32-bit result, with appropriate sign, nonzero, carry, and overflow bits set. Higher precision (48 bits, 64 bits, etc.)~can be obtained in a similar way. When $\.{OP}=1$ and $\.{MOD}=10$, the source value is subtracted from the destination register. Again, \.S, \.N, \.K, and \.V are set; the \.K value in this case represents the ``borrow'' bit. An auxiliary subtraction operation, having $\.{MOD}=11$, subtracts also the current value of \.K, thereby allowing for correct 32-bit subtraction. The operations for $\.{OP}=1$ and $\.{MOD}=12$, 13, and~14 are ``reserved for future expansion.'' Actually they will never change, however, since this RISC chip is purely academic. If you check out the logic below, you will find that they simply set the destination register and the four status bits all to zero. A final operation, called \.{JUMP}, will be explained momentarily. It has $\.{OP}=1$ and $\.{MOD}=15$. It does not affect \.S, \.N, \.K, or~\.V. If the RISC is made with fewer than 16 registers, the higher-numbered ones will effectively contain zero whenever their values are fetched. But if you use them as destination registers, you will set \.S, \.N, \.K, and~\.V as if actual numbers were being stored. Register 0 is different from the other 15 registers: It is the location of the current instruction. Therefore if you change the contents of register~0, you are changing the control flow of the program. If you do not change register~0, it automatically increases by~1. Special treatment occurs when $\.A=3$ and $\.{SRC}=0$. In such a case, the normal rules given above say that the source value should be the contents of the memory location specified by register~0. But that memory location holds the current instruction; so the machine uses the {\sl following\/} location instead, as a 16-bit source operand. If the contents of register~0 are not changed by such a two-word instruction, register~0 will increase by~2 instead of~1. We have now discussed everything about the machine except the operation of the \.{JUMP} command. This command moves the source value to register~0, thereby changing the flow of control. Furthermore, if $\.{DST}\ne0$, it also sets register \.{DST} to the location of the instruction following the \.{JUMP}. Assembly language programmers will recognize this as a convenient way to jump to a subroutine. Example programs can be found in the {\sc TAKE\_\,RISC} module, which includes a simple subroutine for multiplication and division. @ A few auxiliary functions will ameliorate the task of constructing the RISC logic. First comes a routine that ``christens'' a new gate, assigning it a name and a type. The name is constructed from a prefix and a serial number, where the prefix indicates the current portion of logic being created. @<Internal...@>= static Vertex* new_vert(t) char t; /* the type of the new gate */ {@+register Vertex *v; v=next_vert++; if (count<0) v->name=gb_save_string(prefix); else { sprintf(name_buf,"%s%ld",prefix,count); v->name=gb_save_string(name_buf); count++; } v->typ=t; return v; } @ @d start_prefix(s) strcpy(prefix,s);@+count=0 @d numeric_prefix(a,b) sprintf(prefix,"%c%ld:",a,b);@+count=0; @<Private...@>= static Vertex* next_vert; /* the first vertex not yet assigned a name */ static char prefix[5]; /* prefix string for vertex names */ static long count; /* serial number for vertex names */ static char name_buf[100]; /* place to form vertex names */ @ Here are some trivial routines to create gates with 2, 3, or more arguments. The arcs from such a gate to its inputs are assigned length 100. Other routines, defined below, assign length~1 to the arc between an inverter and its unique input. This convention makes the lengths of shortest paths in the resulting network a bit more interesting than they would otherwise be. @d DELAY 100L @<Internal...@>= static Vertex* make2(t,v1,v2) char t; /* the type of the new gate */ Vertex *v1,*v2; {@+register Vertex *v=new_vert(t); gb_new_arc(v,v1,DELAY); gb_new_arc(v,v2,DELAY); return v; } @# static Vertex* make3(t,v1,v2,v3) char t; /* the type of the new gate */ Vertex *v1,*v2,*v3; {@+register Vertex *v=new_vert(t); gb_new_arc(v,v1,DELAY); gb_new_arc(v,v2,DELAY); gb_new_arc(v,v3,DELAY); return v; } @# static Vertex* make4(t,v1,v2,v3,v4) char t; /* the type of the new gate */ Vertex *v1,*v2,*v3,*v4; {@+register Vertex *v=new_vert(t); gb_new_arc(v,v1,DELAY); gb_new_arc(v,v2,DELAY); gb_new_arc(v,v3,DELAY); gb_new_arc(v,v4,DELAY); return v; } @# static Vertex* make5(t,v1,v2,v3,v4,v5) char t; /* the type of the new gate */ Vertex *v1,*v2,*v3,*v4,*v5; {@+register Vertex *v=new_vert(t); gb_new_arc(v,v1,DELAY); gb_new_arc(v,v2,DELAY); gb_new_arc(v,v3,DELAY); gb_new_arc(v,v4,DELAY); gb_new_arc(v,v5,DELAY); return v; } @ We use utility field |w.V| to store a pointer to the complement of a gate, if that complement has been formed. This trick prevents the creation of excessive gates that are equivalent to each other. The following subroutine returns a pointer to the complement of a given gate. @d bar w.V /* field pointing to complement, if known to exist */ @d even_comp(s,v) ((s)&1? v: comp(v)) @<Internal...@>= static Vertex* comp(v) Vertex *v; {@+register Vertex *u; if (v->bar) return v->bar; u=next_vert++; u->bar=v;@+v->bar=u; sprintf(name_buf,"%s~",v->name); u->name=gb_save_string(name_buf); u->typ=NOT; gb_new_arc(u,v,1L); return u; } @ To create a gate for the {\sc EXCLUSIVE-OR} of two arguments, we can either construct the {\sc OR} of two {\sc AND}s or the {\sc AND} of two {\sc OR}s. We choose the former alternative: @<Internal...@>= static Vertex* make_xor(u,v) Vertex *u,*v; {@+register Vertex *t1,*t2; t1=make2(AND,u,comp(v)); t2=make2(AND,comp(u),v); return make2(OR,t1,t2); } @ OK, let's get going. @<Initialize |new_graph|...@>= if (regs<2 || regs>16) regs=16; new_graph=gb_new_graph(1400+115*regs); if (new_graph==NULL) panic(no_room); /* out of memory before we're even started */ sprintf(new_graph->id,"risc(%lu)",regs); strcpy(new_graph->util_types,"ZZZIIVZZZZZZZA"); next_vert=new_graph->vertices; @ @<Add the RISC data to |new_graph|@>= @<Create the inputs and latches@>; @<Create gates for instruction decoding@>; @<Create gates for fetching the source value@>; @<Create gates for the general logic operation@>; @<Create gates for the conditional load operations@>; @<Create gates for the arithmetic operations@>; @<Create gates that bring everything together properly@>; if (next_vert!=new_graph->vertices+new_graph->n) panic(impossible); /* oops, we miscounted; this should be impossible */ @ Internal names will make it convenient to refer to the most important gates. Here are the names of inputs and latches. @<Local variables for |risc|@>= Vertex *run_bit; /* the \.{RUN} input */ Vertex *mem[16]; /* 16 bits of input from read-only memory */ Vertex *prog; /* first of 10 bits in the program register */ Vertex *sign; /* the latched value of \.S */ Vertex *nonzero; /* the latched value of \.N */ Vertex *carry; /* the latched value of \.K */ Vertex *overflow; /* the latched value of \.V */ Vertex *extra; /* latched status bit: are we doing an extra memory cycle? */ Vertex *reg[16]; /* the least-significant bit of a given register */ @ @d first_of(n,t) new_vert(t);@+for (k=1;k<n;k++)@+new_vert(t); @<Create the inputs and latches@>= strcpy(prefix,"RUN");@+count=-1;@+run_bit=new_vert('I'); start_prefix("M");@+for (k=0;k<16;k++)@+mem[k]=new_vert('I'); start_prefix("P");@+prog=first_of(10,'L'); strcpy(prefix,"S");@+count=-1;@+sign=new_vert('L'); strcpy(prefix,"N");@+nonzero=new_vert('L'); strcpy(prefix,"K");@+carry=new_vert('L'); strcpy(prefix,"V");@+overflow=new_vert('L'); strcpy(prefix,"X");@+extra=new_vert('L'); for (r=0;r<regs;r++) { numeric_prefix('R',r); reg[r]=first_of(16,'L'); } @ The order of evaluation of function arguments is not defined in \CEE/, so we introduce a few macros that force left-to-right order. @d do2(result,t,v1,v2) {@+t1=v1;@+t2=v2; result=make2(t,t1,t2);@+} @d do3(result,t,v1,v2,v3) {@+t1=v1;@+t2=v2;@+t3=v3; result=make3(t,t1,t2,t3);@+} @d do4(result,t,v1,v2,v3,v4) {@+t1=v1;@+t2=v2;@+t3=v3;@+t4=v4; result=make4(t,t1,t2,t3,t4);@+} @d do5(result,t,v1,v2,v3,v4,v5) {@+t1=v1;@+t2=v2;@+t3=v3;@+t4=v4;@+t5=v5; result=make5(t,t1,t2,t3,t4,t5);@+} @<Local variables for |risc|@>= Vertex *t1,*t2,*t3,*t4,*t5; /* temporary holds to force evaluation order */ Vertex *tmp[16]; /* additional holding places for partial results */ Vertex *imm; /* is the source value immediate (a given constant)? */ Vertex *rel; /* is the source value relative to the current destination register? */ Vertex *dir; /* should the source value be fetched directly from a source register? */ Vertex *ind; /* should the source value be fetched indirectly from memory? */ Vertex *op; /* least significant bit of \.{OP} */ Vertex *cond; /* most significant bit of \.{OP} */ Vertex *mod[4]; /* the \.{MOD} bits */ Vertex *dest[4]; /* the \.{DEST} bits */ @ The sixth line of the program here can be translated into the logic equation $$ |op|=(|extra|\land|prog|)\lor(\mskip1mu\overline{|extra|}\land|mem[6]|)\,.$$ Once you see why, you'll be able to read the rest of this curious code. @<Create gates for instruction decoding@>= start_prefix("D"); do3(imm,AND,comp(extra),comp(mem[4]),comp(mem[5])); /* $\.A=0$ */ do3(rel,AND,comp(extra),mem[4],comp(mem[5])); /* $\.A=1$ */ do3(dir,AND,comp(extra),comp(mem[4]),mem[5]); /* $\.A=2$ */ do3(ind,AND,comp(extra),mem[4],mem[5]); /* $\.A=3$ */ do2(op,OR,make2(AND,extra,prog),make2(AND,comp(extra),mem[6])); do2(cond,OR,make2(AND,extra,prog+1),make2(AND,comp(extra),mem[7])); for (k=0;k<4;k++) { do2(mod[k],OR,make2(AND,extra,prog+2+k),make2(AND,comp(extra),mem[8+k])); do2(dest[k],OR,make2(AND,extra,prog+6+k),make2(AND,comp(extra),mem[12+k])); } @ @<Create gates for fetching the source value@>= start_prefix("F"); @<Set |old_dest| to the present value of the destination register@>; @<Set |old_src| to the present value of the source register@>; @<Set |inc_dest| to |old_dest| plus \.{SRC}@>; for (k=0;k<16;k++)@/ do4(source[k],OR, make2(AND,imm,mem[k<4?k:3]), make2(AND,rel,inc_dest[k]),@| make2(AND,dir,old_src[k]), make2(AND,extra,mem[k])); @ Here and in the immediately following section we create {\sc OR} gates |old_dest[k]| and |old_src[k]| that might have as many as 16~inputs. (The actual number of inputs is |regs|.) All other gates in the network will have at most five inputs. @<Set |old_dest| to the present value of the destination register@>= for (r=0;r<regs;r++) @/ do4(dest_match[r],AND,even_comp(r,dest[0]),even_comp(r>>1,dest[1]),@| even_comp(r>>2,dest[2]),even_comp(r>>3,dest[3])); for (k=0;k<16;k++) { for (r=0;r<regs;r++)@/ tmp[r]=make2(AND,dest_match[r],reg[r]+k); old_dest[k]=new_vert(OR); for (r=0;r<regs;r++) gb_new_arc(old_dest[k],tmp[r],DELAY); } @ @<Set |old_src| to the present value of the source register@>= for (k=0;k<16;k++) { for (r=0;r<regs;r++)@/ do5(tmp[r],AND,reg[r]+k,even_comp(r,mem[0]),even_comp(r>>1,mem[1]), even_comp(r>>2,mem[2]),even_comp(r>>3,mem[3])); old_src[k]=new_vert(OR); for (r=0;r<regs;r++) gb_new_arc(old_src[k],tmp[r],DELAY); } @ @<Local variables for |risc|@>= Vertex *dest_match[16]; /* |dest_match[r]==1| iff $\.{DST}=r$ */ Vertex *old_dest[16]; /* contents of destination register before operation */ Vertex *old_src[16]; /* contents of source register before operation */ Vertex *inc_dest[16]; /* |old_dest| plus the \.{SRC} field */ Vertex *source[16]; /* source value for the operation */ Vertex *log[16]; /* result of general logic operation */ Vertex *shift[18]; /* result of shift operation, with carry and overflow */ Vertex *sum[18]; /* |old_dest| plus |source| plus optional carry */ Vertex *diff[18]; /* |old_dest| minus |source| minus optional borrow */ Vertex *next_loc[16]; /* contents of register 0, plus 1 */ Vertex *next_next_loc[16]; /* contents of register 0, plus 2 */ Vertex *result[18]; /* result of operating on |old_dest| and |source| */ @ @<Create gates for the general logic operation@>= start_prefix("L"); for (k=0;k<16;k++)@/ do4(log[k],OR,@| make3(AND,mod[0],comp(old_dest[k]),comp(source[k])),@| make3(AND,mod[1],comp(old_dest[k]),source[k]),@| make3(AND,mod[2],old_dest[k],comp(source[k])),@| make3(AND,mod[3],old_dest[k],source[k])); @ @<Create gates for the conditional load operations@>= start_prefix("C"); do4(tmp[0],OR,@| make3(AND,mod[0],comp(sign),comp(nonzero)),@| make3(AND,mod[1],comp(sign),nonzero),@| make3(AND,mod[2],sign,comp(nonzero)),@| make3(AND,mod[3],sign,nonzero)); do4(tmp[1],OR,@| make3(AND,mod[0],comp(carry),comp(overflow)),@| make3(AND,mod[1],comp(carry),overflow),@| make3(AND,mod[2],carry,comp(overflow)),@| make3(AND,mod[3],carry,overflow)); do3(change,OR,comp(cond),make2(AND,tmp[0],comp(op)),make2(AND,tmp[1],op)); @ @<Local variables for |risc|@>= Vertex *change; /* is the destination register supposed to change? */ @ Hardware is like software except that it performs all the operations all the time and then selects only the results it needs. (If you think about it, this is a profound observation about economics, society, and nature. Gosh.) @<Create gates that bring everything together properly@>= start_prefix("Z"); @<Create gates for the |next_loc| and |next_next_loc| bits@>; @<Create gates for the |result| bits@>; @<Create gates for the new values of registers 1 to |regs|@>; @<Create gates for the new values of \.S, \.N, \.K, and \.V@>; @<Create gates for the new values of the program register and |extra|@>; @<Create gates for the new values of register 0 and the memory address register@>; @ @<Create gates for the |next_loc|...@>= next_loc[0]=comp(reg[0]);@+next_next_loc[0]=reg[0]; next_loc[1]=make_xor(reg[0]+1,reg[0]);@+next_next_loc[1]=comp(reg[0]+1); for (t5=reg[0]+1,k=2;k<16;t5=make2(AND,t5,reg[0]+k++)) { next_loc[k]=make_xor(reg[0]+k,make2(AND,reg[0],t5)); next_next_loc[k]=make_xor(reg[0]+k,t5); } @ @<Create gates for the |result| bits@>= jump=make5(AND,op,mod[0],mod[1],mod[2],mod[3]); /* assume |cond=0| */ for (k=0;k<16;k++) { do5(result[k],OR,@| make2(AND,comp(op),log[k]),@| make2(AND,jump,next_loc[k]),@| make3(AND,op,comp(mod[3]),shift[k]),@| make5(AND,op,mod[3],comp(mod[2]),comp(mod[1]),sum[k]),@| make5(AND,op,mod[3],comp(mod[2]),mod[1],diff[k])); do2(result[k],OR,@| make3(AND,cond,change,source[k]),@| make2(AND,comp(cond),result[k])); } for (k=16;k<18;k++) /* carry and overflow bits of the result */ do3(result[k],OR,@| make3(AND,op,comp(mod[3]),shift[k]),@| make5(AND,op,mod[3],comp(mod[2]),comp(mod[1]),sum[k]),@| make5(AND,op,mod[3],comp(mod[2]),mod[1],diff[k])); @ The program register |prog| and the |extra| bit are needed for the case when we must spend an extra cycle to fetch a word from memory. On the first cycle, |ind| is true, so a ``result'' is calculated but not actually used. On the second cycle, |extra| is true. A slight optimization has been introduced in order to make the circuit a bit more interesting: If a conditional load instruction occurs with indirect addressing and a false condition, the extra cycle is not taken. (The |next_next_loc| values were computed for this reason.) @d latchit(u,@!latch) (latch)->alt=make2(AND,u,run_bit) /* |u&run_bit| is new value for |latch| */ @<Create gates for the new values of the program reg...@>= for (k=0;k<10;k++) latchit(mem[k+6],prog+k); do2(nextra,OR,make2(AND,ind,comp(cond)),make2(AND,ind,change)); latchit(nextra,extra); nzs=make4(OR,mem[0],mem[1],mem[2],mem[3]); nzd=make4(OR,dest[0],dest[1],dest[2],dest[3]); @ @<Local variables for |risc|@>= Vertex *jump; /* is this command a \.{JUMP}, assuming |cond| is false? */ Vertex *nextra; /* must we take an extra cycle? */ Vertex *nzs; /* is the \.{SRC} field nonzero? */ Vertex *nzd; /* is the \.{DST} field nonzero? */ @ @<Create gates for the new values of registers 1 to |regs|@>= t5=make2(AND,change,comp(ind)); /* should destination register change? */ for (r=1;r<regs;r++) { t4=make2(AND,t5,dest_match[r]); /* should register |r| change? */ for (k=0;k<16;k++) { do2(t3,OR,make2(AND,t4,result[k]),make2(AND,comp(t4),reg[r]+k)); latchit(t3,reg[r]+k); } } @ @<Create gates for the new values of \.S, \.N, \.K, and \.V@>= do4(t5,OR,@| make2(AND,sign,cond),@| make2(AND,sign,jump),@| make2(AND,sign,ind),@| make4(AND,result[15],comp(cond),comp(jump),comp(ind))); latchit(t5,sign); do4(t5,OR,@| make4(OR,result[0],result[1],result[2],result[3]),@| make4(OR,result[4],result[5],result[6],result[7]),@| make4(OR,result[8],result[9],result[10],result[11]),@| make4(OR,result[12],result[13],result[14],@| @t\hskip5em@>make5(AND,make2(OR,nonzero,sign),op,mod[0],comp(mod[2]),mod[3]))); do4(t5,OR,@| make2(AND,nonzero,cond),@| make2(AND,nonzero,jump),@| make2(AND,nonzero,ind),@| make4(AND,t5,comp(cond),comp(jump),comp(ind))); latchit(t5,nonzero); do5(t5,OR,@| make2(AND,overflow,cond),@| make2(AND,overflow,jump),@| make2(AND,overflow,comp(op)),@| make2(AND,overflow,ind),@| make5(AND,result[17],comp(cond),comp(jump),comp(ind),op)); latchit(t5,overflow); do5(t5,OR,@| make2(AND,carry,cond),@| make2(AND,carry,jump),@| make2(AND,carry,comp(op)),@| make2(AND,carry,ind),@| make5(AND,result[16],comp(cond),comp(jump),comp(ind),op)); latchit(t5,carry); @ As usual, we have left the hardest case for last, hoping that we will have learned enough tricks to handle it when the time of reckoning finally arrives. The most subtle part of the logic here is perhaps the case of a \.{JUMP} command with $\.A=3$. We want to increase register~0 by~1 during the first cycle of such a command, if $\.{SRC}=0$, so that the |result| will be correct on the next cycle. @<Create gates for the new values of register 0...@>= skip=make2(AND,cond,comp(change)); /* false conditional? */ hop=make2(AND,comp(cond),jump); /* \.{JUMP} command? */ do4(normal,OR,@| make2(AND,skip,comp(ind)),@| make2(AND,skip,nzs),@| make3(AND,comp(skip),ind,comp(nzs)),@| make3(AND,comp(skip),comp(hop),nzd)); special=make3(AND,comp(skip),ind,nzs); for (k=0;k<16;k++) { do4(t5,OR,@| make2(AND,normal,next_loc[k]),@| make4(AND,skip,ind,comp(nzs),next_next_loc[k]),@| make3(AND,hop,comp(ind),source[k]),@| make5(AND,comp(skip),comp(hop),comp(ind),comp(nzd),result[k])); do2(t4,OR,@| make2(AND,special,reg[0]+k),@| make2(AND,comp(special),t5)); latchit(t4,reg[0]+k); do2(t4,OR,@| make2(AND,special,old_src[k]),@| make2(AND,comp(special),t5)); {@+register Arc *a=gb_virgin_arc(); a->tip=make2(AND,t4,run_bit); a->next=new_graph->outs; new_graph->outs=a; /* pointer to memory address bit */ } } /* arcs for output bits will appear in big-endian order */ @ @<Local variables for |risc|@>= Vertex *skip; /* are we skipping a conditional load operation? */ Vertex *hop; /* are we doing a \.{JUMP}? */ Vertex *normal; /* is this a case where register 0 is simply incremented? */ Vertex *special; /* is this a case where register 0 and the memory address register will not coincide? */ @* Serial addition. We haven't yet specified the parts of |risc| that deal with addition and subtraction; somehow, those parts wanted to be separate from the rest. To complete our mission, we will use subroutine calls of the form `|make_adder(n,x,y,z,carry,add)|', where |x| and |y| are |n|-bit arrays of input gates and |z|~is an |(n+1)|-bit array of output gates. If |add!=0|, the subroutine computes |x+y|, otherwise it computes |x-y|. If |carry!=0|, the |carry| gate is effectively added to~|y| before the operation. A simple |n|-stage serial scheme, which reduces the problem of |n|-bit addition to |(n-1)|-bit addition, is adequate for our purposes here. (A parallel adder, which gains efficiency by reducing the problem size from |n| to~$n/\phi$, can be found in the |prod| routine below.) The handy identity $x-y=\overline{\overline x+y}$ is used to reduce subtraction to addition. @<Internal...@>= static void make_adder(n,x,y,z,carry,add) unsigned long n; /* number of bits */ Vertex *x[],*y[]; /* input gates */ Vertex *z[]; /* output gates */ Vertex *carry; /* add this to |y|, unless it's null */ char add; /* should we add or subtract? */ {@+register long k; Vertex *t1,*t2,*t3,*t4; /* temporary storage used by |do4| */ if (!carry) { z[0]=make_xor(x[0],y[0]); carry=make2(AND,even_comp(add,x[0]),y[0]); k=1; }@+else k=0; for (;k<n;k++) { comp(x[k]);@+comp(y[k]);@+comp(carry); /* generate inverse gates */ do4(z[k],OR,@| make3(AND,x[k],comp(y[k]),comp(carry)),@| make3(AND,comp(x[k]),y[k],comp(carry)),@| make3(AND,comp(x[k]),comp(y[k]),carry),@| make3(AND,x[k],y[k],carry)); do3(carry,OR,@| make2(AND,even_comp(add,x[k]),y[k]),@| make2(AND,even_comp(add,x[k]),carry),@| make2(AND,y[k],carry)); } z[n]=carry; } @ OK, now we can add. What good does that do us? In the first place, we need a 4-bit adder to compute the least significant bits of $|old_dest|+\.{SRC}$. The other 12 bits of that sum are simpler. @<Set |inc_dest| to |old_dest| plus \.{SRC}@>= make_adder(4L,old_dest,mem,inc_dest,NULL,1); up=make2(AND,inc_dest[4],comp(mem[3])); /* remaining bits must increase */ down=make2(AND,comp(inc_dest[4]),mem[3]); /* remaining bits must decrease */ for (k=4;;k++) { comp(up);@+comp(down); do3(inc_dest[k],OR,@| make2(AND,comp(old_dest[k]),up),@| make2(AND,comp(old_dest[k]),down),@| make3(AND,old_dest[k],comp(up),comp(down))); if (k<15) { up=make2(AND,up,old_dest[k]); down=make2(AND,down,comp(old_dest[k])); }@+else break; } @ @<Local variables for |risc|@>= Vertex *up,*down; /* gates used when computing |inc_dest| */ @ In the second place, we need a 16-bit adder and a 16-bit subtracter for the four addition/subtraction commands. @<Create gates for the arithmetic operations@>= start_prefix("A"); @<Create gates for the shift operations@>; make_adder(16L,old_dest,source,sum,make2(AND,carry,mod[0]),1); /* adder */ make_adder(16L,old_dest,source,diff,make2(AND,carry,mod[0]),0); /* subtracter */ do2(sum[17],OR,@| make3(AND,old_dest[15],source[15],comp(sum[15])),@| make3(AND,comp(old_dest[15]),comp(source[15]),sum[15])); /* overflow */ do2(diff[17],OR,@| make3(AND,old_dest[15],comp(source[15]),comp(diff[15])),@| make3(AND,comp(old_dest[15]),source[15],diff[15])); /* overflow */ @ @<Create gates for the shift operations@>= for (k=0;k<16;k++)@/ do4(shift[k],OR,@| (k==0? make4(AND,source[15],mod[0],comp(mod[1]),comp(mod[2])):@| @t\hskip5em@>make3(AND,source[k-1],comp(mod[1]),comp(mod[2]))),@| (k<4? make4(AND,source[k+12],mod[0],mod[1],comp(mod[2])):@| @t\hskip5em@>make3(AND,source[k-4],mod[1],comp(mod[2]))),@| (k==15? make4(AND,source[15],comp(mod[0]),comp(mod[1]),mod[2]):@| @t\hskip5em@>make3(AND,source[k+1],comp(mod[1]),mod[2])),@| (k>11? make4(AND,source[15],comp(mod[0]),mod[1],mod[2]):@| @t\hskip5em@>make3(AND,source[k+4],mod[1],mod[2]))); do4(shift[16],OR,@| make2(AND,comp(mod[2]),source[15]),@| make3(AND,comp(mod[2]),mod[1], make3(OR,source[14],source[13],source[12])),@| make3(AND,mod[2],comp(mod[1]),source[0]),@| make3(AND,mod[2],mod[1],source[3])); /* ``carry'' */ do3(shift[17],OR,@| make3(AND,comp(mod[2]),comp(mod[1]), make_xor(source[15],source[14])),@| make4(AND,comp(mod[2]),mod[1],@| @t\hskip5em@>make5(OR,source[15],source[14], source[13],source[12],source[11]),@| @t\hskip5em@>make5(OR,comp(source[15]),comp(source[14]), comp(source[13]),@| @t\hskip10em@>comp(source[12]),comp(source[11]))),@| make3(AND,mod[2],mod[1], make3(OR,source[0],source[1],source[2]))); /* ``overflow'' */ @* RISC management. The |run_risc| procedure takes a gate graph output by |risc| and simulates its behavior, given the contents of its read-only memory. (See the demonstration program {\sc TAKE\_\,RISC}, which appears in a module by itself, for a typical illustration of how |run_risc| might be used.) This procedure clears the simulated machine and begins executing the program that starts at address~0. It stops when it gets to an address greater than the size of read-only memory supplied. One way to stop it is therefore to execute a command such as |0x0f00|, which will transfer control to location |0xffff|; even better is |0x0f8f|, which transfers to location |0xffff| without changing the status of \.S and \.N. However, if the given read-only memory contains a full set of $2^{16}$ words, |run_risc| will never stop. When |run_risc| does stop, it returns 0 and puts the final contents of the simulated registers into the global array |risc_state|. Or, if |g| was not a decent graph, |run_risc| returns a negative value and leaves |risc_state| untouched. @<The |run_risc|...@>= long run_risc(g,rom,size,trace_regs) Graph *g; /* graph output by |risc| */ unsigned long rom[]; /* contents of read-only memory */ unsigned long size; /* length of |rom| vector */ unsigned long trace_regs; /* if nonzero, this many registers will be traced */ {@+register unsigned long l; /* memory address */ register unsigned long m; /* memory or register contents */ register Vertex *v; /* the current gate of interest */ register Arc *a; /* the current output list element of interest */ register long k,r; /* general-purpose indices */ long x,s,n,c,o; /* status bits */ if (trace_regs) @<Print a headline@>; r=gate_eval(g,"0",NULL); /* reset the RISC by turning off the \.{RUN} bit */ if (r<0) return r; /* not a valid gate graph! */ g->vertices->val=1; /* turn the \.{RUN} bit on */ while (1) { for (a=g->outs,l=0;a;a=a->next) l=2*l+a->tip->val; /* set $l=\null$memory address */ if (trace_regs) @<Print register contents@>; if (l>=size) break; /* stop if memory check occurs */ for (v=g->vertices+1,m=rom[l];v<=g->vertices+16;v++,m>>=1) v->val=m&1; /* store bits of memory word in the input gates */ gate_eval(g,NULL,NULL); /* do another RISC cycle */ } if (trace_regs) @<Print a footline@>; @<Dump the register contents into |risc_state|@>; return 0; } @ If tracing is requested, we write on the standard output file. @<Print a headline@>= { for (r=0;r<trace_regs;r++) printf(" r%-2ld ",r); /* register names */ printf(" P XSNKV MEM\n"); /* |prog|, |extra|, status bits, memory */ } @ @<Print a footline@>= printf("Execution terminated with memory address %04lx.\n",l); @ Here we peek inside the circuit to see what values are about to be latched. @<Print register contents@>= { for (r=0;r<trace_regs;r++) { v=g->vertices+(16*r+47); /* most significant bit of register |r| */ m=0; if (v->typ=='L') for (k=0,m=0;k<16;k++,v--) m=2*m+v->alt->val; printf("%04lx ",m); } for (k=0,m=0,v=g->vertices+26;k<10;k++,v--) m=2*m+v->alt->val; /* |prog| */ x=(g->vertices+31)->alt->val; /* |extra| */ s=(g->vertices+27)->alt->val; /* |sign| */ n=(g->vertices+28)->alt->val; /* |nonzero| */ c=(g->vertices+29)->alt->val; /* |carry| */ o=(g->vertices+30)->alt->val; /* |overflow| */ printf("%03lx%c%c%c%c%c ",m<<2, x?'X':'.', s?'S':'.', n?'N':'.', c?'K':'.', o?'V':'.'); if (l>=size) printf("????\n"); else printf("%04lx\n",rom[l]); } @ @<Dump...@>= for (r=0;r<16;r++) { v=g->vertices+(16*r+47); /* most significant bit of register |r| */ m=0; if (v->typ=='L') for (k=0,m=0;k<16;k++,v--) m=2*m+v->alt->val; risc_state[r]=m; } for (k=0,m=0,v=g->vertices+26;k<10;k++,v--) m=2*m+v->alt->val; /* |prog| */ m=4*m+(g->vertices+31)->alt->val; /* |extra| */ m=2*m+(g->vertices+27)->alt->val; /* |sign| */ m=2*m+(g->vertices+28)->alt->val; /* |nonzero| */ m=2*m+(g->vertices+29)->alt->val; /* |carry| */ m=2*m+(g->vertices+30)->alt->val; /* |overflow| */ risc_state[16]=m; /* program register and status bits go here */ risc_state[17]=l; /* this is the out-of-range address that caused termination */ @ @<Global variables@>= unsigned long risc_state[18]; @*Generalized gate graphs. For intermediate computations, it is convenient to allow two additional types of gates: {\advance\parindent 2em \smallskip \item{|'C'|} denotes a constant gate of value |z.I|. \smallskip \item{|'='|} denotes a copy of a previous gate; utility field |alt| points to that previous gate. \smallskip}\noindent Such gates might appear anywhere in the graph, possibly interspersed with the inputs and latches. Here is a simple subroutine that prints a symbolic representation of a generalized gate graph on the standard output file: @d bit z.I /* field containing the constant value of a |'C'| gate */ @d print_gates p_gates /* abbreviation makes chopped-off name unique */ @<The |print_gates| routine@>= static void pr_gate(v) Vertex *v; {@+register Arc *a; printf("%s = ",v->name); switch(v->typ) { case 'I':printf("input");@+break; case 'L':printf("latch"); if (v->alt) printf("ed %s",v->alt->name); break; case '~':printf("~ ");@+break; case 'C':printf("constant %ld",v->bit); break; case '=':printf("copy of %s",v->alt->name); } for (a=v->arcs;a;a=a->next) { if (a!=v->arcs) printf(" %c ",(char)v->typ); printf(a->tip->name); } printf("\n"); } @# void print_gates(g) Graph *g; {@+register Vertex *v; register Arc *a; for (v=g->vertices;v<g->vertices+g->n;v++) pr_gate(v); for (a=g->outs;a;a=a->next) if (is_boolean(a->tip)) printf("Output %ld\n",the_boolean(a->tip)); else printf("Output %s\n",a->tip->name); } @ @(gb_gates.h@>= #define bit @t\quad@> z.I @ The |reduce| routine takes a generalized graph |g| and uses the identities $\overline{\overline x}=x$ and $$\openup1\jot \vbox{\halign{\hfil$x#0=\null$&$#$,\hfil\quad &\hfil$x#1=\null$&$#$,\hfil\quad &\hfil$x#x=\null$&$#$,\hfil\quad &\hfil$x#\overline x=\null$&$#$,\hfil\cr \land&0&\land&x&\land&x&\land&0\cr \lor&x&\lor&1&\lor&x&\lor&1\cr \oplus&x&\oplus&\overline x&\oplus&0&\oplus&1\cr}}$$ to create an equivalent graph having no |'C'| or |'='| or obviously redundant gates. The reduced graph also excludes any gates that are not used directly or indirectly in the computation of the output values. @<Internal...@>= static Graph* reduce(g) Graph *g; {@+register Vertex *u, *v; /* the current vertices of interest */ register Arc *a, *b; /* the current arcs of interest */ Arc *aa, *bb; /* their predecessors */ Vertex *latch_ptr; /* top of the latch list */ long n=0; /* the number of marked gates */ Graph *new_graph; /* the reduced gate graph */ Vertex *next_vert=NULL, *max_next_vert=NULL; /* allocation of new vertices */ Arc *avail_arc=NULL; /* list of recycled arcs */ Vertex *sentinel; /* end of the vertices */ if (g==NULL) panic(missing_operand); /* where is |g|? */ sentinel=g->vertices+g->n; while (1) { latch_ptr=NULL; for (v=g->vertices;v<sentinel;v++) @<Reduce gate |v|, if possible, or put it on the latch list@>; @<Check to see if any latch has become constant; if not, |break|@>; } @<Mark all gates that are used in some output@>; @<Copy all marked gates to a new graph@>; gb_recycle(g); return new_graph; } @ We will link latches together via their |v.V| fields. @<Check to see if any latch has become constant; if not, |break|@>= {@+char no_constants_yet=1; for (v=latch_ptr;v;v=v->v.V) { u=v->alt; /* the gate whose value will be latched */ if (u->typ=='=') v->alt=u->alt; else if (u->typ=='C') { v->typ='C';@+v->bit=u->bit;@+no_constants_yet=0; } } if (no_constants_yet) break; } @ @d foo x.V /* link field used to find all the gates later */ @<Reduce gate |v|, if possible, or put it on the latch list@>= { switch(v->typ) { case 'L': v->v.V=latch_ptr;@+latch_ptr=v;@+break; case 'I': case 'C': break; case '=': u=v->alt; if (u->typ=='=') v->alt=u->alt; else if (u->typ=='C') { v->bit=u->bit;@+goto make_v_constant; } break; case NOT:@<Try to reduce an inverter, then |goto done|@>; case AND:@<Try to reduce an {\sc AND} gate@>;@+goto test_single_arg; case OR:@<Try to reduce an {\sc OR} gate@>;@+goto test_single_arg; case XOR:@<Try to reduce an {\sc EXCLUSIVE-OR} gate@>; test_single_arg: if (v->arcs->next) break; v->alt=v->arcs->tip; make_v_eq: v->typ='=';@+goto make_v_arcless; make_v_1: v->bit=1;@+goto make_v_constant; make_v_0: v->bit=0; make_v_constant: v->typ='C'; make_v_arcless: v->arcs=NULL; } v->bar=NULL; /* this field will point to the complement, if computed later */ done: v->foo=v+1; /* this field will link all the vertices together */ } @ @<Try to reduce an inverter...@>= u=v->arcs->tip; if (u->typ=='=') u=v->arcs->tip=u->alt; if (u->typ=='C') { v->bit=1-u->bit;@+goto make_v_constant; }@+else if (u->bar) { /* this inverse already computed */ v->alt=u->bar;@+goto make_v_eq; }@+else { u->bar=v;@+v->bar=u;@+goto done; } @ @<Try to reduce an {\sc AND} gate@>= for (a=v->arcs,aa=NULL;a;a=a->next) { u=a->tip; if (u->typ=='=') u=a->tip=u->alt; if (u->typ=='C') { if (u->bit==0) goto make_v_0; goto bypass_and; }@+else@+for (b=v->arcs;b!=a;b=b->next) { if (b->tip==u) goto bypass_and; if (b->tip==u->bar) goto make_v_0; } aa=a;@+continue; bypass_and: if (aa) aa->next=a->next; else v->arcs=a->next; } if (v->arcs==NULL) goto make_v_1; @ @<Try to reduce an {\sc OR} gate@>= for (a=v->arcs,aa=NULL;a;a=a->next) { u=a->tip; if (u->typ=='=') u=a->tip=u->alt; if (u->typ=='C') { if (u->bit) goto make_v_1; goto bypass_or; }@+else@+for (b=v->arcs;b!=a;b=b->next) { if (b->tip==u) goto bypass_or; if (b->tip==u->bar) goto make_v_1; } aa=a;@+continue; bypass_or: if (aa) aa->next=a->next; else v->arcs=a->next; } if (v->arcs==NULL) goto make_v_0; @ @<Try to reduce an {\sc EXCLUSIVE-OR} gate@>= {@+long cmp=0; for (a=v->arcs,aa=NULL;a;a=a->next) { u=a->tip; if (u->typ=='=') u=a->tip=u->alt; if (u->typ=='C') { if (u->bit) cmp=1-cmp; goto bypass_xor; }@+else@+for (bb=NULL,b=v->arcs;b!=a;b=b->next) { if (b->tip==u) goto double_bypass; if (b->tip==u->bar) { cmp=1-cmp; goto double_bypass; } bb=b;@+ continue; double_bypass: if (bb) bb->next=b->next; else v->arcs=b->next; goto bypass_xor; } aa=a;@+ continue; bypass_xor: if (aa) aa->next=a->next; else v->arcs=a->next; a->a.A=avail_arc; avail_arc=a; } if (v->arcs==NULL) { v->bit=cmp; goto make_v_constant; } if (cmp) @<Complement one argument of |v|@>; } @ @<Complement one argument of |v|@>= { for (a=v->arcs;;a=a->next) { u=a->tip; if (u->bar) break; /* good, the complement is already known */ if (a->next==NULL) { /* oops, this is our last chance */ @<Create a new vertex for complement of |u|@>; break; } } a->tip=u->bar; } @ Here we've come to a subtle point: If a lot of |XOR| gates involve an input that is set to the constant value~1, the ``reduced'' graph might actually be larger than the original, in the sense of having more vertices (although fewer arcs). Therefore we must have the ability to allocate new vertices during the reduction phase of |reduce|. At least one arc has been added to the |avail_arc| list whenever we reach this portion of the program. @<Create a new vertex for complement of |u|@>= if (next_vert==max_next_vert) { next_vert=gb_typed_alloc(7,Vertex,g->aux_data); if (next_vert==NULL) { gb_recycle(g); panic(no_room+1); /* can't get auxiliary storage! */ } max_next_vert=next_vert+7; } next_vert->typ=NOT; sprintf(name_buf,"%s~",u->name); next_vert->name=gb_save_string(name_buf); next_vert->arcs=avail_arc; /* this is known to be non-|NULL| */ avail_arc->tip=u; avail_arc=avail_arc->a.A; next_vert->arcs->next=NULL; next_vert->bar=u; next_vert->foo=u->foo; u->foo=u->bar=next_vert++; @ During the marking phase, we will use the |w.V| field to link the list of nodes-to-be-marked. That field will turn out to be non-|NULL| only in the marked nodes. (We no longer use its former meaning related to complementation, so we call it |lnk| instead of |bar|.) @d lnk w.V /* stack link for marking */ @<Mark all gates that are used in some output@>= { for (v=g->vertices;v!=sentinel;v=v->foo) v->lnk=NULL; for (a=g->outs;a;a=a->next) { v=a->tip; if (is_boolean(v)) continue; if (v->typ=='=') v=a->tip=v->alt; if (v->typ=='C') { /* this output is constant, so make it boolean */ a->tip=(Vertex*)v->bit; continue; } @<Mark all gates that are used to compute |v|@>; } } @ @<Mark all gates that are used to compute |v|@>= if (v->lnk==NULL) { v->lnk=sentinel; /* |v| now represents the top of the stack of nodes to be marked */ do@+{ n++; b=v->arcs; if (v->typ=='L') { u=v->alt; /* latch vertices have a ``hidden'' dependency */ if (u<v) n++; /* latched input value will get a special gate */ if (u->lnk==NULL) { u->lnk=v->lnk; v=u; }@+else v=v->lnk; }@+else v=v->lnk; for (;b;b=b->next) { u=b->tip; if (u->lnk==NULL) { u->lnk=v; v=u; } } }@+while (v!=sentinel); } @ It is easier to copy a directed acyclic graph than to copy a general graph, but we do have to contend with the feedback in latches. @d reverse_arc_list(@!alist) {@+for (aa=alist,b=NULL;aa;b=aa,aa=a) { a=aa->next; aa->next=b; } alist=b;@+} @<Copy all marked gates to a new graph@>= new_graph=gb_new_graph(n); if (new_graph==NULL) { gb_recycle(g); panic(no_room+2); /* out of memory */ } strcpy(new_graph->id,g->id); strcpy(new_graph->util_types,"ZZZIIVZZZZZZZA"); next_vert=new_graph->vertices; for (v=g->vertices,latch_ptr=NULL;v!=sentinel;v=v->foo) { if (v->lnk) { /* yes, |v| is marked */ u=v->lnk=next_vert++; /* make note of where we've copied it */ @<Make |u| a copy of |v|; put it on the latch list if it's a latch@>; } } @<Fix up the |alt| fields of the newly copied latches@>; reverse_arc_list(g->outs); for (a=g->outs;a;a=a->next) { b=gb_virgin_arc(); b->tip=is_boolean(a->tip)? a->tip: a->tip->lnk; b->next=new_graph->outs; new_graph->outs=b; } @ @<Make |u| a copy of |v|; put it on the latch list if it's a latch@>= u->name=gb_save_string(v->name); u->typ=v->typ; if (v->typ=='L') { u->alt=latch_ptr;@+latch_ptr=v; } reverse_arc_list(v->arcs); for (a=v->arcs;a;a=a->next) gb_new_arc(u,a->tip->lnk,a->len); @ @<Fix up the |alt| fields of the newly copied latches@>= while (latch_ptr) { u=latch_ptr->lnk; /* the copy of a latch */ v=u->alt; u->alt=latch_ptr->alt->lnk; latch_ptr=v; if (u->alt<u) @<Replace |u->alt| by a new gate that copies an input@>; } @ Suppose we had a latch whose value was originally the {\sc AND} of two inputs, where one of those inputs has now been set to~1. Then the latch should still refer to a subsequent gate, equal to the value of the other input on the previous cycle. We create such a gate here, making it an {\sc OR} of two identical inputs. We do this because we're not supposed to leave any |'='| in the result of |reduce|, and because every {\sc OR} is supposed to have at least two inputs. @<Replace |u->alt| by a new gate that copies an input@>= { v=u->alt; /* the input gate that should be copied for latching */ u->alt=next_vert++; sprintf(name_buf,"%s>%s",v->name,u->name); u=u->alt; u->name=gb_save_string(name_buf); u->typ=OR; gb_new_arc(u,v,DELAY);@+gb_new_arc(u,v,DELAY); } @* Parallel multiplication. Now comes the |prod| routine, which constructs a rather different network of gates, based this time on a divide-and-conquer paradigm. Let's take a breather before we tackle it. (Deep breath.) The subroutine call |prod(m,n)| creates a network for the binary multiplication of unsigned |m|-bit numbers by |n|-bit numbers, assuming that |m>=2| and |n>=2|. There is no upper limit on the sizes of |m| and~|n|, except of course the limits imposed by the size of memory in which this routine is run. The overall strategy used by |prod| is to start with a generalized gate graph for multiplication in which many of the gates are identically zero or copies of other gates. Then the |reduce| routine will perform local optimizations leading to the desired result. Since there are no latches, some of the complexities of the general |reduce| routine are avoided. All of the |AND|, |OR|, and |XOR| gates of the network returned by |prod| have exactly two inputs. The depth of the circuit (i.e., the length of its longest path) is $3\log m/\!\log 1.5 + \log(m+n)/\!\log\phi +O(1)$, where $\phi=(1+\sqrt5\,)/2$ is the golden ratio. The grand total number of gates is $6mn+5m^2+O\bigl((m+n)\log(m+n)\bigr)$. There is a demonstration program called {\sc MULTIPLY} that uses |prod| to compute products of large integers. @<The |prod| routine@>= Graph* prod(m,n) unsigned long m,n; /* lengths of the binary numbers to be multiplied */ {@+@<Local variables for |prod|@>@; @# if (m<2) m=2; if (n<2) n=2; @<Allocate space for a temporary graph |g| and for auxiliary tables@>; @<Fill |g| with generalized gates that do parallel multiplication@>; if (gb_trouble_code) { gb_recycle(g);@+panic(alloc_fault); /* too big */ } g=reduce(g); return g; /* if |g==NULL|, the |panic_code| was set by |reduce| */ } @ The divide-and-conquer recurrences used in this network lead to interesting patterns. First we use a method for parallel column addition that reduces the sum of three numbers to the sum of two numbers. Repeated use of this reduction makes it possible to reduce the sum of |m| numbers to a sum of just two numbers, with a total circuit depth that satisfies the recurrence $T(3N)=T(2N)+O(1)$. Then when the result has been reduced to a sum of two numbers, we use a parallel addition scheme based on recursively ``golden sectioning the data''; in other words, the recursion partitions the data into two parts such that the ratio of the larger part to the smaller part is approximately $\phi$. This technique proves to be slightly better than a binary partition would be, both asymptotically and for small values of~$m+n$. \def\flog{\mathop{\rm flog}\nolimits} We define $\flog N$, the Fibonacci logarithm of~$N$, to be the smallest @^Fibonacci, Leonardo, numbers@> nonnegative integer~$k$ such that $N\le F_{k+1}$. Let $N=m+n$. Our parallel adder for two numbers of $N$ bits will turn out to have depth at most $2+\flog N$. The unreduced graph~|g| in our circuit for multiplication will have fewer than $(6m+3\flog N)N$ gates. @<Allocate space for a temporary graph |g| and for auxiliary tables@>= m_plus_n=m+n;@+@<Compute $f=\flog(m+n)$@>; g=gb_new_graph((6*m-7+3*f)*m_plus_n); if (g==NULL) panic(no_room); /* out of memory before we're even started */ sprintf(g->id,"prod(%lu,%lu)",m,n); strcpy(g->util_types,"ZZZIIVZZZZZZZA"); long_tables=gb_typed_alloc(2*m_plus_n+f,long,g->aux_data); vert_tables=gb_typed_alloc(f*m_plus_n,Vertex*,g->aux_data); if (gb_trouble_code) { gb_recycle(g); panic(no_room+1); /* out of memory trying to create auxiliary tables */ } @ @<Local variables for |prod|@>= unsigned long m_plus_n; /* guess what this variable holds */ long f; /* initially $\flog(m+n)$, later flog of other things */ Graph *g; /* graph of generalized gates, to be reduced eventually */ long *long_tables; /* beginning of auxiliary array of |long| numbers */ Vertex **vert_tables; /* beginning of auxiliary array of gate pointers */ @ @<Compute $f=\flog(m+n)$@>= f=4;@+j=3;@+k=5; /* $j=F_f$, $k=F_{f+1}$ */ while (k<m_plus_n) { k=k+j; j=k-j; f++; } @ The well-known formulas for a ``full adder,'' $$ x+y+z=s+2c,\qquad \hbox{where $s=x\oplus y\oplus z$ and $c=xy\lor yz\lor zx$},$$ can be applied to each bit of an $N$-bit number, thereby providing us with a way to reduce the sum of three numbers to the sum of two. The input gates of our network will be called $x_0$, $x_1$, \dots,~$x_{m-1}$, $y_0$,~$y_1$, \dots,~$y_{n-1}$, and the outputs will be called $z_0$, $z_1$, \dots,~$z_{m+n-1}$. The logic of the |prod| network will compute $$(z_{m+n-1}\ldots z_1z_0)_2=(x_{m-1}\ldots x_1x_0)_2\cdot (y_{n-1}\ldots y_1y_0)_2\,,$$ by first considering the product to be the $m$-fold sum $A_0+A_1+\cdots+A_{m-1}$, where $$A_j=2^jx_j\cdot(y_{n-1}\ldots y_1y_0)_2\,,\qquad 0\le j<m.$$ Then the three-to-two rule for addition is used to define further numbers $A_m$, $A_{m+1}$, \dots,~$A_{3m-5}$ by the scheme $$A_{m+2j}+A_{m+2j+1}=A_{3j}+A_{3j+1}+A_{3j+2}\,,\qquad 0\le j\le m-3.$$ [A similar but slightly less efficient scheme was used by Pratt and Stockmeyer in {\sl Journal of Computer and System Sciences \bf12} (1976), @^Pratt, Vaughan Ronald@> @^Stockmeyer, Larry Joseph@> Proposition~5.3. The recurrence used here is related to the Josephus @^Josephus, Flavius, problem@> problem with step-size~3; see {\sl Concrete Mathematics}, {\mathhexbox278}3.3.] For this purpose, we compute intermediate results $P_j$, $Q_j$, and~$R_j$ by the rules $$\eqalign{P_j&=A_{3j}\oplus A_{3j+1}\,;\cr Q_j&=A_{3j}\land A_{3j+1}\,;\cr A_{m+2j}&=P_j\oplus A_{3j+2}\,;\cr R_j&=P_j\land A_{3j+2}\,;\cr A_{m+2j+1}&=2(Q_j\lor R_j)\,.\cr}$$ Finally we let $$\eqalign{U&=A_{3m-6}\oplus A_{3m-5}\,,\cr V&=A_{3m-6}\land A_{3m-5}\,;\cr}$$ these are the values that would be $P_{m-2}$ and $Q_{m-2}$ if the previous formulas were allowed to run past $j=m-3$. The final result $Z=(z_{m+n-1}\ldots z_1z_0)_2$ can now be expressed as $$Z=U+2V\,.$$ The gates of the first part of the network are conveniently obtained in groups of $N=m+n$, representing the bits of the quantities $A_j$, $P_j$, $Q_j$, $R_j$, $U$, and~$V$. We will put the least significant bit of $A_j$ in gate position |g->vertices+a(j)*N|, where $a(j)=j+1$ for $0\le j<m$ and $a(m+2j+t)=m+5j+3+2t$ for $0\le j\le m-3$, $0\le t\le1$. @<Fill |g| with generalized gates that do parallel multiplication@>= next_vert=g->vertices; start_prefix("X");@+x=first_of(m,'I'); start_prefix("Y");@+y=first_of(n,'I'); @<Define $A_j$ for $0\le j<m$@>; @<Define $P_j$, $Q_j$, $A_{m+2j}$, $R_j$, and $A_{m+2j+1}$ for $0\le j\le m-3$@>; @<Define $U$ and $V$@>; @<Compute the final result $Z$ by parallel addition@>; @ @<Local variables for |prod|@>= register long i,j,k,l; /* all-purpose indices */ register Vertex *v; /* current vertex of interest */ Vertex *x,*y; /* least-significant bits of the input gates */ Vertex *alpha,*beta; /* least-significant bits of arguments */ @ @<Define $A_j$ for $0\le j<m$@>= for (j=0; j<m; j++) { numeric_prefix('A',j); for (k=0; k<j; k++) { v=new_vert('C');@+v->bit=0; /* this gate is the constant 0 */ } for (k=0; k<n; k++) make2(AND,x+j,y+k); for (k=j+n; k<m_plus_n; k++) { v=new_vert('C');@+v->bit=0; /* this gate is the constant 0 */ } } @ Since |m| is |unsigned|, it is necessary to say `|j<m-2|' here instead of `|j<=m-3|'. @d a_pos(j) (j<m? j+1: m+5*((j-m)>>1)+3+(((j-m)&1)<<1)) @<Define $P_j$, $Q_j$, $A_{m+2j}$, $R_j$, and $A_{m+2j+1}$...@>= for (j=0; j<m-2; j++) { alpha=g->vertices+(a_pos(3*j)*m_plus_n); beta=g->vertices+(a_pos(3*j+1)*m_plus_n); numeric_prefix('P',j); for (k=0; k<m_plus_n; k++) make2(XOR,alpha+k,beta+k); numeric_prefix('Q',j); for (k=0; k<m_plus_n; k++) make2(AND,alpha+k,beta+k); alpha=next_vert-2*m_plus_n; beta=g->vertices+(a_pos(3*j+2)*m_plus_n); numeric_prefix('A',(long)m+2*j); for (k=0; k<m_plus_n; k++) make2(XOR,alpha+k,beta+k); numeric_prefix('R',j); for (k=0; k<m_plus_n; k++) make2(AND,alpha+k,beta+k); alpha=next_vert-3*m_plus_n; beta=next_vert-m_plus_n; numeric_prefix('A',(long)m+2*j+1); v=new_vert('C');@+v->bit=0; /* another 0, it multiplies $Q\lor R$ by 2 */ for (k=0; k<m_plus_n-1; k++) make2(OR,alpha+k,beta+k); } @ Actually $v_{m+n-1}$ will never be used (it has to be zero); but we compute it anyway. We don't have to worry about such nitty gritty details because |reduce| will get rid of all the obvious redundancy. @<Define $U$ and $V$@>= alpha=g->vertices+(a_pos(3*m-6)*m_plus_n); beta=g->vertices+(a_pos(3*m-5)*m_plus_n); start_prefix("U"); for (k=0; k<m_plus_n; k++) make2(XOR,alpha+k,beta+k); start_prefix("V"); for (k=0; k<m_plus_n; k++) make2(AND,alpha+k,beta+k); @* Parallel addition. It's time now to take another deep breath. We have finished the parallel multiplier except for one last step, the design of a parallel adder. The adder is based on the following theory: We want to perform the binary addition $$\vbox{\halign{\hfil$#$&&\ \hfil$#$\cr u_{N-1}&\ldots&u_2&u_1&u_0\cr v_{N-2}&\ldots&v_1&v_0\cr \noalign{\kern2pt\hrule\kern4pt} z_{N-1}&\ldots&z_2&z_1&z_0\cr}}$$ where we know that $u_k+v_k\le1$ for all~$k$. It follows that $z_k=u_k\oplus w_k$, where $w_0=0$ and $$ w_k\;=\;v_{k-1}\;\lor\;u_{k-1}v_{k-2}\;\lor\;u_{k-1}u_{k-2}v_{k-3}\;\lor \;\cdots\;\lor\;u_{k-1}\ldots u_1v_0$$ for $k>0$. The problem has therefore been reduced to the evaluation of $w_1$, $w_2$, \dots, $w_{N-1}$. Let $c_k^{\,j}$ denote the {\sc OR} of the first $j$ terms in the formula that defines $w_k$, and let $d_k^{\,j}$ denote the $j$-fold product $u_{k-1}u_{k-2}\ldots u_{k-j}$. Then $w_k=c_k^k$, and we can use a recursive scheme of the form $$c_k^{\,j}=c_k^{\,i}\lor d_k^{\,i}c_{k-i}^{\,j-i}\,,\qquad d_k^{\,j}=d_k^{\,i}d_{k-i}^{\,j-i}\,,\qquad j\ge2,$$ to do the evaluation. \def\down{\mathop{\rm down}} It turns out that this recursion behaves very nicely if we choose $i=\down[j]$, where $\down[j]$ is defined for $j>1$ by the formula $$\down[j]\;=\;j-F_{(\flog j)-1}\,.$$ For example, $\flog18=7$ because $F_7=13<18\le21=F_8$, hence $\down[18]=18-F_6=10$. Let us write $j\to\down[j]$, and consider the oriented tree on the set of all positive integers that is defined by this relation. One of the paths in this tree, for example, is $18\to10\to5\to3\to2\to1$. Our recurrence for $w_{18}=c_{18}^{18}$ involves $c_{18}^{10}$, which involves $c_{18}^5$, which involves $c_{18}^3$, and so on. In general, we will compute $c_k^{\,j}$ for all $j$ with $k\to^*j$, and we will compute $d_k^{\,j}$ for all $j$ with $k\to^+j$. It is not difficult to prove that $$k\;\to^*\;j\;\to\;i\qquad\hbox{implies}\qquad k-i\;\to^*\;j-i\,;$$ therefore the auxiliary factors $c_{k-i}^{\,j-i}$ and $d_{k-i}^{\,j-i}$ needed in the recurrence scheme will already have been evaluated. (Indeed, one can prove more: Let $l=\flog k$. If the complete path from $k$ to~$1$ in the tree is $k=k_0\to k_1\to\cdots\to k_t=1$, then the differences $k_0-k_1$, $k_1-k_2$, \dots, $k_{t-2}-k_{t-1}$ will consist of precisely the Fibonacci numbers $F_{l-1}$, $F_{l-2}$, \dots,~$F_2$, except for the numbers that appear when $F_{l+1}-k$ is written as a sum of non-consecutive Fibonacci numbers.) It can also be shown that, when $k>1$, we have $$\flog k=\min_{0<j<n}\,\max\bigl(1+\flog j,\,2+\flog(k-j)\bigr)\,,$$ and that $\down[k]$ is the smallest~$j$ such that the minimum is achieved in this equation. Therefore the depth of the circuit for computing $w_k$ from the $u$'s and~$v$'s is exactly $\flog k$. In particular, we can be sure that at most $3\flog N$ gates will be created when computing $z_k$, and that there will be at most $3N\flog N$ gates in the parallel addition portion of the circuit. @<Compute the final result $Z$ by parallel addition@>= @<Set up auxiliary tables to handle Fibonacci-based recurrences@>; @<Create the gates for $W$, remembering intermediate results that might be reused later@>; @<Compute the last gates $Z=U\oplus W$, and record their locations as outputs of the network@>; g->n=next_vert-g->vertices; /* reduce to the actual number of gates used */ @ After we have created a gate for $w_k$, we will store its address as the value of $w[k]$ in an auxiliary table. After we've created a gate for $c_k^{\,i}$ where $i<k$ is a Fibonacci number~$F_{l+1}$ and $l=\flog i\ge2$, we will store its address as the value of $c[k+(l-2)N]$; the gate $d_k^{\,i}$ will immediately follow this one. Tables of $\flog j$ and $\down[j]$ will facilitate all these manipulations. @<Set up auxiliary tables to handle Fibonacci-based recurrences@>= w=vert_tables; c=w+m_plus_n; flog=long_tables; down=flog+m_plus_n+1; anc=down+m_plus_n; flog[1]=0;@+flog[2]=2; down[1]=0;@+down[2]=1; for (i=3,j=2,k=3,l=3; l<=m_plus_n; l++) { if (l>k) { k=k+j; j=k-j; i++; /* $F_i=j<l\le k=F_{i+1}$ */ } flog[l]=i; down[l]=l-k+j; } @ @<Local variables for |prod|@>= Vertex *uu, *vv; /* pointer to $u_0$ and $v_0$ */ Vertex **w; /* table of pointers to $w_k$ */ Vertex **c; /* table of pointers to potentially important intermediate values $c_k^{\,i}$ */ Vertex *cc,*dd; /* pointers to $c_k^{\,i}$ and $d_k^{\,i}$ */ long *flog; /* table of flog values */ long *down; /* table of down values */ long *anc; /* table of ancestors of the current $k$ */ @ @<Create the gates for $W$, remembering intermediate results that might be reused later@>= vv=next_vert-m_plus_n;@+uu=vv-m_plus_n; start_prefix("W"); v=new_vert('C');@+v->bit=0;@+w[0]=v; /* $w_0=0$ */ v=new_vert('=');@+v->alt=vv;@+w[1]=v; /* $w_1=v_0$ */ for (k=2;k<m_plus_n;k++) { @<Set the |anc| table to a list of the ancestors of |k| in decreasing order, stopping with |anc[l]=2|@>; i=1;@+cc=vv+k-1;@+dd=uu+k-1; while (1) { j=anc[l]; /* now $i=\down[j]$ */ @# @<Compute the gate $b_k^{\,j}=d_k^{\,i}\land c_{k-i}^{\,j-i}$@>; @<Compute the gate $c_k^{\,j}=c_k^{\,i}\lor b_k^{\,j}$@>; if (flog[j]<flog[j+1]) /* $j$ is a Fibonacci number */ c[k+(flog[j]-2)*m_plus_n]=v; if (l==0) break; cc=v; @<Compute the gate $d_k^{\,j}=d_k^{\,i}\land d_{k-i}^{\,j-i}$@>; dd=v; i=j; l--; } w[k]=v; } @ If $k\to j$, we call $j$ an ``ancestor'' of $k$ because we are thinking of the tree defined by `$\to$'; this tree is rooted at $2\to1$. @<Set the |anc| table to a list of the ancestors of |k| in decreasing order, stopping with |anc[l]=2|@>= for (l=0,j=k;;l++,j=down[j]) { anc[l]=j; if (j==2) break; } @ @d spec_gate(v,a,k,j,t) v=next_vert++; sprintf(name_buf,"%c%ld:%ld",a,k,j); v->name=gb_save_string(name_buf); v->typ=t; @<Compute the gate $b_k^{\,j}=d_k^{\,i}\land c_{k-i}^{\,j-i}$@>= spec_gate(v,'B',k,j,AND); gb_new_arc(v,dd,DELAY); /* first argument is $d_k^{\,i}$ */ f=flog[j-i]; /* get ready to compute the second argument, $c_{k-i}^{\,j-i}$ */ gb_new_arc(v,f>0? c[k-i+(f-2)*m_plus_n]:vv+k-i-1,DELAY); @ @<Compute the gate $c_k^{\,j}=c_k^{\,i}\lor b_k^{\,j}$@>= if (l) { spec_gate(v,'C',k,j,OR); }@+else v=new_vert(OR); /* if $l$ is zero, this gate is $c_k^k=w_k$ */ gb_new_arc(v,cc,DELAY); /* first argument is $c_k^{\,i}$ */ gb_new_arc(v,next_vert-2,DELAY); /* second argument is $b_k^{\,j}$ */ @ Here we reuse the value $f=\flog(j-i)$ computed a minute ago. @<Compute the gate $d_k^{\,j}=d_k^{\,i}\land d_{k-i}^{\,j-i}$@>= spec_gate(v,'D',k,j,AND); gb_new_arc(v,dd,DELAY); /* first argument is $d_k^{\,i}$ */ gb_new_arc(v,f>0? c[k-i+(f-2)*m_plus_n]+1:uu+k-i-1,DELAY); /* $d_{k-i}^{\,j-i}$ */ @ The output list will contain the gates in ``big-endian order'' $z_{m+n-1}$, \dots, $z_1$, $z_0$, because we insert them into the |outs| list in little-endian order. @<Compute the last gates $Z=U\oplus W$...@>= start_prefix("Z"); for (k=0;k<m_plus_n;k++) {@+register Arc *a=gb_virgin_arc(); a->tip=make2(XOR,uu+k,w[k]); a->next=g->outs; g->outs=a; } @* Partial evaluation. The subroutine call |partial_gates(g,r,prob,seed,buf)| creates a new gate graph from a given gate graph~|g| by ``partial evaluation,'' i.e., by setting some of the inputs to constant values and simplifying the result. The new graph is usually smaller than |g|; it might, in fact, be a great deal smaller. Graph~|g| is destroyed in the process. The first |r| inputs of |g| are retained unconditionally. Each remaining input is retained with probability |prob/65536|, and if not retained it is assigned a random constant value. For example, about half of the inputs will become constant if |prob=32768|. The |seed| parameter defines a machine-independent source of random numbers, and it may be given any value between $0$ and $2^{31}-1$. If the |buf| parameter is non-null, it should be the address of a string. In such a case, |partial_gates| will put a record of its partial evaluation into that string; |buf| will contain one character for each input gate after the first |r|, namely |'*'| if the input was retained, |'0'| if it was set to~$0$, or |'1'| if it was set to~$1$. The new graph will contain only gates that contribute to the computation of at least one output value. Therefore some input gates might disappear even though they were supposedly ``retained,'' i.e., even though their value has not been set constant. The |name| field of a vertex can be used to determine exactly which input gates have survived. If graph |g| was created by |risc|, users will probably want to make |r>=1|, since the whole RISC circuit collapses to zero whenever its first input `\.{RUN}' is set to 0. An interesting class of graphs is produced by the function call |partial_gates(prod(m,n),m,0,seed,NULL)|, which creates a graph corresponding to a circuit that multiplies a given |m|-bit number by a fixed (but randomly selected) |n|-bit constant. If the constant is not zero, all |m| of the ``retained'' input gates necessarily survive. The demo program called {\sc MULTIPLY} illustrates such circuits. The graph |g| might be a generalized network; that is, it might involve the |'C'| or |'='| gates described earlier. Notice that if |r| is sufficiently large, |partial_gates| becomes equivalent to the |reduce| routine. Therefore we need not make that private routine public. As usual, the result will be |NULL|, and |panic_code| will be set, if |partial_gates| is unable to complete its task. @<The |partial_gates| routine@>= Graph *partial_gates(g,r,prob,seed,buf) Graph *g; /* generalized gate graph */ unsigned long r; /* the number of initial gates to leave untouched */ unsigned long prob; /* scaled probability of not touching subsequent input gates */ long seed; /* seed value for random number generation */ char *buf; /* optional parameter for information about partial assignment */ {@+register Vertex *v; /* the current gate of interest */ if (g==NULL) panic(missing_operand); /* where is |g|? */ gb_init_rand(seed); /* get them random numbers rolling */ for (v=g->vertices+r;v<g->vertices+g->n;v++) switch (v->typ) { case 'C': case '=': continue; /* input gates might still follow */ case 'I': if ((gb_next_rand()>>15)>=prob) { v->typ='C';@+v->bit=gb_next_rand()>>30; if (buf) *buf++=v->bit+'0'; }@+else if (buf) *buf++='*'; break; default: goto done; /* no more input gates can follow */ } done:if (buf) *buf=0; /* terminate the string */ g=reduce(g); @<Give the reduced graph a suitable |id|@>; return g; /* if |(g==NULL)|, a |panic_code| has been set by |reduce| */ } @ The |buf| parameter is not recorded in the graph's |id| field, since it has no effect on the graph itself. @<Give the reduced graph a suitable |id|@>= if (g) { strcpy(name_buf,g->id); if (strlen(name_buf)>54) strcpy(name_buf+51,"..."); sprintf(g->id,"partial_gates(%s,%lu,%lu,%ld)",name_buf,r,prob,seed); } @* Index. Here is a list that shows where the identifiers of this program are defined and used. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gb_lisa.w�������������������������������������������������������������������������������������������0000444�0001750�0001750�00000066124�06236445251�011006� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_\,LISA} \prerequisites{GB\_\,GRAPH}{GB\_\,IO} @* Introduction. This GraphBase module contains the |lisa| subroutine, which creates rectangular matrices of data based on Leonardo da Vinci's @^Vinci, Leonardo da@> {\sl Gioconda\/} (aka Mona Lisa). It also contains the |plane_lisa| subroutine, which constructs undirected planar graphs based on |lisa|, and the |bi_lisa| subroutine, which constructs undirected bipartite graphs. Another example of the use of |lisa| can be found in the demo program {\sc ASSIGN\_LISA}. @d plane_lisa p_lisa /* abbreviation for Procrustean external linkage */ @(gb_lisa.h@>= #define plane_lisa p_lisa extern long* lisa(); extern Graph *plane_lisa(); extern Graph *bi_lisa(); @ The subroutine call |lisa(m,n,d,m0,m1,n0,n1,d0,d1,area)| constructs an $m\times n$ matrix of integers in the range $[0\,.\,.\,d\mskip1mu]$, based on the information in \.{lisa.dat}. Storage space for the matrix is allocated in the memory area called |area|, using the normal GraphBase conventions explained in {\sc GB\_\,GRAPH}. The entries of the matrix can be regarded as pixel data, with 0~representing black and $d$~representing white, and with intermediate values representing shades of gray. The data in \.{lisa.dat} has 360 rows and 250 columns. The rows are numbered 0 to 359 from top to bottom, and the columns are numbered 0 to 249 from left to right. The output of |lisa| is generated from a rectangular section of the picture consisting of |m1-m0| rows and |n1-n0| columns; more precisely, |lisa| uses the data in positions $(k,l)$ for |m0<=k<m1| and |n0<=l<n1|. One way to understand the process of mapping |M=m1-m0| rows and |N=n1-n0| columns of input into |m|~rows and |n|~columns of output is to imagine a giant matrix of $mM$ rows and $nN$ columns in which the original input data has been replicated as an $M\times N$ array of submatrices of size $m\times n$; each of the submatrices contains $mn$ identical pixel values. We can also regard the giant matrix as an $m\times n$ array of submatrices of size $M\times N$. The pixel values to be output are obtained by averaging the $M_{}N$ pixel values in the submatrices of this second interpretation. More precisely, the output pixel value in a given row and column is obtained in two steps. First we sum the $M_{}N$ entries in the corresponding submatrix of the giant matrix, obtaining a value $D$ between 0 and~$255M_{}N$. Then we scale the value~$D$ linearly into the desired final range $[0\,.\,.\,d\mskip1mu]$ by setting the result to~0 if |D<d0|, to~$d$ if |D>=d1|, and to $\lfloor d(D-|d0|)/(|d1|-|d0|)\rfloor$ if |d0<=D<d1|. @d MAX_M 360 /* the total number of rows of input data */ @d MAX_N 250 /* the total number of columns of input data */ @d MAX_D 255 /* maximum pixel value in the input data */ @ Default parameter values are automatically substituted when |m|, |n|, |d|, |m1|, |n1|, and/or |d1| are given as~0: If |m1=0| or |m1>360|, |m1|~is changed to 360; if |n1=0| or |n1>250|, |n1|~is changed to~250. Then if |m| is zero, it is changed to~|m1-m0|; if |n| is zero, it is changed to~|n1-n0|. If |d| is zero, it is changed to~255. If |d1| is zero, it is changed to |255(m1-m0)(n1-n0)|. After these substitutions have been made, the parameters must satisfy $$\hbox{|m0<m1|, \qquad|n0<n1|, \qquad and \qquad |d0<d1|.}$$ Examples: The call |@t\\{lisa\_pix}@>=lisa(0,0,0,0,0,0,0,0,0,area)| is equivalent to the call |@t\\{lisa\_pix}@>=lisa(360,250,255,0,360,0,250,0,255*360*250,area)|; this special case delivers the original \.{lisa.dat} data as a $360\times250$ array of integers in the range $[0\,.\,.\,255]$. You can access the pixel in row~$k$ and column~$l$ by writing $$\hbox{|*(@[@t\\{lisa\_pix}@>@]+n*k+l)|}\,,$$ where |n| in this case is 250. A square array extracted from the top part of the picture, leaving out Mona's hands at the bottom, can be obtained by calling |lisa(250,250,255,0,250,0,250,0,0,area)|. The call |lisa(36,25,25500,0,0,0,0,0,0,area)| gives a $36\times25$ array of pixel values in the range $[0\,.\,.\,25500]$, obtained by summing $10\times10$ subsquares of the original data. The call |lisa(100,100,100,0,0,0,0,0,0,area)| gives a $100\times100$ array of pixel values in the range $[0\,.\,.\,100]$; in this case the original data is effectively broken into subpixels and averaged appropriately. Notice that each output pixel in this example comes from 3.6 input rows and 2.5 input columns; therefore the image is being distorted (compressed vertically). However, our GraphBase applications are generally interested more in combinatorial test data, not in images per~se. If |(m1-m0)/m=(n1-n0)/n|, the output of |lisa| will represent ``square pixels.'' But if |(m1-m0)/m<(n1-n0)/n|, a halftone generated from the output will be compressed in the horizontal dimension; if |(m1-m0)/m>(n1-n0)/n|, it will be compressed in the vertical dimension. If you want to reduce the original image to binary data, with the value~0 wherever the original pixels are less than some threshold value~|t| and the value~1 whenever they are |t| or more, call |lisa(m,n,1,m0,m1,n0,n1,@t}\penalty0{@>0,t*(m1-m0)*(n1-n0),area)|. The subroutine call |lisa(1000,1000,255,0,250,0,250,0,0,area)| produces a million pixels from the upper part of the original image. This matrix contains more entries than the original data in \.{lisa.dat}, but of course it is not any more accurate; it has simply been obtained by linear interpolation---in fact, by replicating the original data in $4\times4$ subarrays. Mona Lisa's famous smile appears in the $16\times32$ subarray defined by |m0=94|, |m1=110|, |n0=97|, |n1=129|. The |smile| macro makes this easily accessible. (See also |eyes|.) A string |lisa_id| is constructed, showing the actual parameter values used by |lisa| after defaults have been supplied. The |area| parameter is omitted from this string. @<gb_lisa.h@>= #define smile @t\quad@> m0=94,m1=110,n0=97,n1=129 /* $16\times32$ */ #define eyes @t\quad@> m0=61,m1=80,n0=91,n1=140 /* $20\times50$ */ extern char lisa_id[]; @ @<Global variables@>= char lisa_id[]= "lisa(360,250,9999999999,359,360,249,250,9999999999,9999999999)"; @ If the |lisa| routine encounters a problem, it returns |NULL| (\.{NULL}), after putting a nonzero number into the external variable |panic_code|. This code number identifies the type of failure. Otherwise |lisa| returns a pointer to the newly created array. (The external variable |panic_code| is defined in {\sc GB\_\,GRAPH}.) @d panic(c) @+{@+panic_code=c;@+gb_trouble_code=0;@+return NULL;@+} @ The \CEE/ file \.{gb\_lisa.c} begins as follows. (Other subroutines come later.) @p #include "gb_io.h" /* we will use the {\sc GB\_\,IO} routines for input */ #include "gb_graph.h" /* we will use the {\sc GB\_\,GRAPH} data structures */ @h@# @<Global variables@>@; @<Private variables@>@; @<Private subroutines@>@; @# long *lisa(m,n,d,m0,m1,n0,n1,d0,d1,area) unsigned long m,n; /* number of rows and columns desired */ unsigned long d; /* maximum pixel value desired */ unsigned long m0,m1; /* input will be from rows $[|m0|\,.\,.\,|m1|)$ */ unsigned long n0,n1; /* and from columns $[|n0|\,.\,.\,|n1|)$ */ unsigned long d0,d1; /* lower and upper threshold of raw pixel scores */ Area area; /* where to allocate the matrix that will be output */ {@+@<Local variables for |lisa|@>@;@# @<Check the parameters and adjust them for defaults@>; @<Allocate the matrix@>; @<Read \.{lisa.dat} and map it to the desired output form@>; return matx; } @ @<Local variables for |lisa|@>= long *matx=NULL; /* the matrix constructed by |lisa| */ register long k,l; /* the current row and column of output */ register long i,j; /* all-purpose indices */ long cap_M,cap_N; /* |m1-m0| and |n1-n0|, dimensions of the input */ long cap_D; /* |d1-d0|, scale factor */ @ @<Check the param...@>= if (m1==0 || m1>MAX_M) m1=MAX_M; if (m1<=m0) panic(bad_specs+1); /* |m0| must be less than |m1| */ if (n1==0 || n1>MAX_N) n1=MAX_N; if (n1<=n0) panic(bad_specs+2); /* |n0| must be less than |n1| */ cap_M=m1-m0;@+cap_N=n1-n0; if (m==0) m=cap_M; if (n==0) n=cap_N; if (d==0) d=MAX_D; if (d1==0) d1=MAX_D*cap_M*cap_N; if (d1<=d0) panic(bad_specs+3); /* |d0| must be less than |d1| */ if (d1>=0x80000000) panic(bad_specs+4); /* |d1| must be less than $2^{31}$ */ cap_D=d1-d0; sprintf(lisa_id,"lisa(%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu,%lu)", m,n,d,m0,m1,n0,n1,d0,d1); @ @<Allocate the matrix@>= matx=gb_typed_alloc(m*n,long,area); if (gb_trouble_code) panic(no_room+1); /* no room for the output data */ @ @<Read \.{lisa.dat} and map it to the desired output form@>= @<Open the data file, skipping unwanted rows at the beginning@>; @<Generate the $m$ rows of output@>; @<Close the data file, skipping unwanted rows at the end@>; @* Elementary image processing. As mentioned in the introduction, we can visualize the input as a giant $mM\times nN$ matrix, into which an $M\times N$ image is placed by replication of pixel values, and from which an $m\times n$ image is derived by summation of pixel values and subsequent scaling. Here |M=m1-m0| and |N=n1-n0|. Let $(\kappa,\lambda)$ be a position in the giant matrix, where $0\le\kappa<mM$ and $0\le\lambda<nN$. The corresponding indices of the input image are then $\bigl(|m0|+\lfloor\kappa/m\rfloor, |n0|+\lfloor\lambda/n\rfloor\bigr)$, and the corresponding indices of the output image are $\bigl(\lfloor\kappa/M\rfloor,\lfloor\lambda/N\rfloor\bigr)$. Our main job is to compute the sum of all pixel values that lie in each given row~|k| and column~|l| of the output image. Many elements are repeated in the sum, so we want to use multiplication instead of simple addition whenever possible. For example, let's consider the inner loop first, the loop on $l$ and $\lambda$. Suppose $n=3$, and suppose the input pixels in the current row of interest are $\langle a_0,\ldots,a_{N-1}\rangle$. Then if $N=3$, we want to compute the output pixels $\langle3a_0,3a_1,3a_2\rangle$; if $N=4$, we want to compute $\langle3a_0+a_1,2a_1+2a_2,a_2+3a_3\rangle$; if $N=2$, we want to compute $\langle2a_0,a_0+a_1,2a_1\rangle$. The logic for doing this computation with the proper timing can be expressed conveniently in terms of four local variables: @<Local variables for |lisa|@>= long *cur_pix; /* current position within |in_row| */ long lambda; /* right boundary in giant for the input pixel in |cur_pix| */ long lam; /* the first giant column not yet used in the current row */ long next_lam; /* right boundary in giant for the output pixel in column~|l| */ @ @<Process one row of pixel sums, multiplying them by~|f|@>= lambda=n;@+cur_pix=in_row+n0; for (l=lam=0; l<n; l++) {@+register long sum=0; next_lam=lam+cap_N; do@+{@+register long nl; /* giant column where something new might happen */ if (lam>=lambda) cur_pix++,lambda+=n; if (lambda<next_lam) nl=lambda; else nl=next_lam; sum+=(nl-lam)*(*cur_pix); lam=nl; }@+while (lam<next_lam); *(out_row+l)+=f*sum; } @ The outer loop (on $k$ and $\kappa$) is similar but slightly more complicated, because it deals with a vector of sums instead of a single sum and because it must invoke the input routine when we're done with a row of input data. %Generate them rows... @<Generate the $m$ rows of output@>= kappa=0; out_row=matx; for (k=kap=0; k<m;k++) { for (l=0;l<n;l++) *(out_row+l)=0; /* clear the vector of sums */ next_kap=kap+cap_M; do@+{@+register long nk; /* giant row where something new might happen */ if (kap>=kappa) { @<Read a row of input into |in_row|@>; kappa+=m; } if (kappa<next_kap) nk=kappa; else nk=next_kap; f=nk-kap; @<Process one...@>; kap=nk; }@+while (kap<next_kap); for (l=0; l<n; l++,out_row++) /* note that |out_row| will advance by~|n| */ @<Scale the sum found in |*out_row|@>; } @ @<Local variables for |lisa|@>= long kappa; /* bottom boundary in giant for the input pixels in |in_row| */ long kap; /* the first giant row not yet used */ long next_kap; /* bottom boundary in giant for the output pixel in row~|k| */ long f; /* factor by which current input sums should be replicated */ long *out_row; /* current position in |matx| */ @* Integer scaling. Here's a general-purpose routine to compute $\lfloor na/b\rfloor$ exactly without risking integer overflow, given integers $n\ge0$ and $0<a\le b$. The idea is to solve the problem first for $n/2$, if $n$ is too large. We are careful to precompute values so that integer overflow cannot occur when $b$ is very large. @d el_gordo 0x7fffffff /* $2^{31}-1$, the largest single-precision |long| */ @<Private sub...@>= static long na_over_b(n,a,b) long n,a,b; {@+long nmax=el_gordo/a; /* the largest $n$ such that $na$ doesn't overflow */ register long r,k,q,br; long a_thresh, b_thresh; if (n<=nmax) return (n*a)/b; a_thresh=b-a; b_thresh=(b+1)>>1; /* $\lceil b/2\rceil$ */ k=0; do@+{@+bit[k]=n&1; /* save the least significant bit of $n$ */ n>>=1; /* and shift it out */ k++; }@+while (n>nmax); r=n*a;@+ q=r/b;@+ r=r-q*b; @<Maintain quotient |q| and remainder |r| while increasing |n| back to its original value $2^kn+(|bit|[k-1]\ldots |bit|[0])_2$@>; return q; } @ @<Private var...@>= static long bit[30]; /* bits shifted out of |n| */ @ @<Maintain quotient...@>= do@+{@+k--;@+ q<<=1; if (r<b_thresh) r<<=1; else q++,br=(b-r)<<1,r=b-br; if (bit[k]) { if (r<a_thresh) r+=a; else q++,r-=a_thresh; } }@+while (k); @ @<Scale the sum found in |*out_row|@>= if (*out_row<=d0) *out_row=0; else if (*out_row>=d1) *out_row=d; else *out_row=na_over_b(d,*out_row-d0,cap_D); @* Input data format. The file \.{lisa.dat} contains 360 rows of pixel data, and each row appears on five consecutive lines of the file. The first four lines contain the data for 60 pixels; each sequence of four pixels is represented by five radix-85 digits, using the |icode| mapping of {\sc GB\_\,IO}. The fifth and final line of each row contains $4+4+2=10$ more pixels, represented as $5+5+3$ radix-85 digits. @<Open the data file, skipping unwanted rows at the beginning@>= if (gb_open("lisa.dat")!=0) panic(early_data_fault); /* couldn't open the file; |io_errors| tells why */ for (i=0;i<m0;i++) for (j=0;j<5;j++) gb_newline(); /* ignore one row of data */ @ @<Close the data file, skipping unwanted rows at the end@>= for (i=m1;i<MAX_M;i++) for (j=0;j<5;j++) gb_newline(); /* ignore one row of data */ if (gb_close()!=0) panic(late_data_fault); /* checksum or other failure in data file; see |io_errors| */ @ @<Read a row of input into |in_row|@>= {@+register long dd; for (j=15,cur_pix=&in_row[0];;cur_pix+=4) { dd=gb_digit(85);@+dd=dd*85+gb_digit(85);@+dd=dd*85+gb_digit(85); if (cur_pix==&in_row[MAX_N-2]) break; dd=dd*85+gb_digit(85);@+dd=dd*85+gb_digit(85); *(cur_pix+3)=dd&0xff;@+dd=(dd>>8)&0xffffff; *(cur_pix+2)=dd&0xff;@+dd>>=8; *(cur_pix+1)=dd&0xff;@+*cur_pix=dd>>8; if (--j==0) gb_newline(),j=15; } *(cur_pix+1)=dd&0xff;@+*cur_pix=dd>>8;@+gb_newline(); } @ @<Private var...@>= static long in_row[MAX_N]; @* Planar graphs. We can obtain a large family of planar graphs based on digitizations of Mona Lisa by using the following simple scheme: Each matrix of pixels defines a set of connected regions containing pixels of the same value. (Two pixels are considered adjacent if they share an edge.) These connected regions are taken to be vertices of an undirected graph; two vertices are adjacent if the corresponding regions have at least one pixel edge in common. We can also state the construction another way. If we take any planar graph and collapse two adjacent vertices, we obtain another planar graph. Suppose we start with the planar graph having $mn$ vertices $[k,l]$ for $0\le k<m$ and $0\le l<n$, where $[k,l]$ is adjacent to $[k,l-1]$ when $l>0$ and to $[k-1,l]$ when $k>0$. Then we can attach pixel values to each vertex, after which we can repeatedly collapse adjacent vertices whose pixel values are equal. The resulting planar graph is the same as the graph of connected regions that was described in the previous paragraph. The subroutine call |plane_lisa(m,n,d,m0,m1,n0,n1,d0,d1)| constructs the planar graph associated with the digitization produced by |lisa|. The description of |lisa|, given earlier, explains the significance of parameters |m|, |n|, |d|, |m0|, |m1|, |n0|, |n1|, |d0|, and |d1|. There will be at most $mn$ vertices, and the graph will be simply an $m\times n$ grid unless |d| is small enough to permit adjacent pixels to have equal values. The graph will also become rather trivial if |d| is too small. Utility fields |first_pixel| and |last_pixel| give, for each vertex, numbers of the form $k*n+l$, identifying the topmost/leftmost and bottommost/rightmost positions $[k,l]$ in the region corresponding to that vertex. Utility fields |matrix_rows| and |matrix_cols| in the |Graph| record contain the values of |m| and~|n|; thus, in particular, the value of |n| needed to decompose |first_pixel| and |last_pixel| into individual coordinates can be found in |g->matrix_cols|. The original pixel value of a vertex is placed into its |pixel_value| utility field. @d pixel_value x.I @d first_pixel y.I @d last_pixel z.I @d matrix_rows uu.I @d matrix_cols vv.I @p Graph *plane_lisa(m,n,d,m0,m1,n0,n1,d0,d1) unsigned long m,n; /* number of rows and columns desired */ unsigned long d; /* maximum value desired */ unsigned long m0,m1; /* input will be from rows $[|m0|\,.\,.\,|m1|)$ */ unsigned long n0,n1; /* and from columns $[|n0|\,.\,.\,|n1|)$ */ unsigned long d0,d1; /* lower and upper threshold of raw pixel scores */ {@+@<Local variables for |plane_lisa|@>@;@# init_area(working_storage); @<Figure out the number of connected regions, |regs|@>; @<Set up a graph with |regs| vertices@>; @<Put the appropriate edges into the graph@>; trouble: gb_free(working_storage); if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } return new_graph; } @ @<Local variables for |plane_lisa|@>= Graph *new_graph; /* the graph constructed by |plane_lisa| */ register long j,k,l; /* all-purpose indices */ Area working_storage; /* tables needed while |plane_lisa| does its thinking */ long *a; /* the matrix constructed by |lisa| */ long regs=0; /* number of vertices generated so far */ @ @<gb_lisa.h@>= #define pixel_value @t\quad@> x.I /* definitions for the header file */ #define first_pixel @t\quad@> y.I #define last_pixel @t\quad@> z.I #define matrix_rows @t\quad@> uu.I #define matrix_cols @t\quad@> vv.I @ The following algorithm for counting the connected regions considers the array elements |a[k,l]| to be linearly ordered as they appear in memory. Thus we can speak of the $n$ elements preceding a given element |a[k,l]|, if $k>0$; these are the elements |a[k,l-1]|, \dots, |a[k,0]|, |a[k-1,n-1]|, \dots, |a[k-1,l]|. These $n$ elements appear in $n$ different columns. During the algorithm, we move through the array from bottom right to top left, maintaining an auxiliary table $\langle f[0],\ldots,f[n-1] \rangle$ with the following significance: Whenever two of the $n$ elements preceding our current position $[k,l]$ are connected to each other by a sequence of pixels with equal value, where the connecting links do not involve pixels more than $n$ steps before our current position, those elements will be linked together in the $f$ array. More precisely, we will have $f[c_1]=c_2$, \dots, $f[c_{j-1}]=c_j$, and $f[c_j]=c_j$, when there are $j$ equivalent elements in columns $c_1$, \dots,~$c_j$. Here $c_1$ will be the ``last'' column and $c_j$ the ``first,'' in wraparound order; each element with $f[c]\ne c$ points to an earlier element. The main function of the |f| table is to identify the topmost/leftmost pixel of a region. If we are at position |[k,l]| and if we find $f[l]=l$ while $a[k-1,l]\ne a[k,l]$, there is no way to connect |[k,l]| to earlier positions, so we create a new vertex for it. We also change the |a| matrix, to facilitate another algorithm below. If position |[k,l]| is the topmost/leftmost pixel of a region, we set |a[k,l]=-1-a[k,l]|; otherwise we set |a[k,l]=f[l]|, the column of a preceding element belonging to the same region. @<Figure out the number...@>= a=lisa(m,n,d,m0,m1,n0,n1,d0,d1,working_storage); if (a==NULL) return NULL; /* |panic_code| has been set by |lisa| */ sscanf(lisa_id,"lisa(%lu,%lu,",&m,&n); /* adjust for defaults */ f=gb_typed_alloc(n,unsigned long,working_storage); if (f==NULL) { gb_free(working_storage); /* recycle the |a| matrix */ panic(no_room+2); /* there's no room for the |f| vector */ } @<Pass over the |a| matrix from bottom right to top left, looking for the beginnings of connected regions@>; @ @<Local variables for |plane_lisa|@>= unsigned long *f; /* beginning of array |f|; $f[j]$ is the column of an equivalent element */ long *apos; /* the location of |a[k,l]| */ @ We maintain a pointer |apos| equal to |&a[k,l]|, so that |*(apos-1)=a[k,l-1]| and |*(apos-n)=a[k-1,l]| when $l>0$ and $k>0$. The loop that replaces $f[j]$ by $j$ can cause this algorithm to take time $mn^2$. We could improve the worst case by using path compression, but the extra complication is rarely worth the trouble. @<Pass over the |a| matrix from bottom right to top left, looking for the beginnings of connected regions@>= for (k=m, apos=a+n*(m+1)-1; k>=0; k--) for (l=n-1; l>=0; l--,apos--) { if (k<m) { if (k>0&&*(apos-n)==*apos) { for (j=l; f[j]!=j; j=f[j]) ; /* find the first element */ f[j]=l; /* link it to the new first element */ *apos=l; }@+else if (f[l]==l) *apos=-1-*apos,regs++; /* new region found */ else *apos=f[l]; } if (k>0&&l<n-1&&*(apos-n)==*(apos-n+1)) f[l+1]=l; f[l]=l; } @ @<Set up a graph with |regs| vertices@>= new_graph=gb_new_graph(regs); if (new_graph==NULL) panic(no_room); /* out of memory before we're even started */ sprintf(new_graph->id,"plane_%s",lisa_id); strcpy(new_graph->util_types,"ZZZIIIZZIIZZZZ"); new_graph->matrix_rows=m; new_graph->matrix_cols=n; @ Now we make another pass over the matrix, this time from top left to bottom right. An auxiliary vector of length |n| is once again sufficient to tell us when one region is adjacent to a previous one. In this case the vector is called |u|, and it contains pointers to the vertices in the $n$ positions before our current position. We assume that a pointer to a |Vertex| takes the same amount of memory as an |unsigned long|, hence |u| can share the space formerly occupied by~|f|; if this is not the case, a system-dependent change should be made here. @^system dependencies@> The vertex names are simply integers, starting with 0. @<Put the appropriate edges into the graph@>= regs=0; u=(Vertex**)f; for (l=0;l<n;l++) u[l]=NULL; for (k=0,apos=a,aloc=0;k<m;k++) for (l=0;l<n;l++,apos++,aloc++) { w=u[l]; if (*apos<0) { sprintf(str_buf,"%ld",regs); v=new_graph->vertices+regs; v->name=gb_save_string(str_buf); v->pixel_value=-*apos-1; v->first_pixel=aloc; regs++; }@+else v=u[*apos]; u[l]=v; v->last_pixel=aloc; if (gb_trouble_code) goto trouble; if (k>0 && v!=w) adjac(v,w); if (l>0 && v!=u[l-1]) adjac(v,u[l-1]); } @ @<Local variables for |pl...@>= Vertex **u; /* table of vertices for previous $n$ pixels */ Vertex *v; /* vertex corresponding to position |[k,l]| */ Vertex *w; /* vertex corresponding to position |[k-1,l]| */ long aloc; /* $k*n+l$ */ @ The |adjac| routine makes two vertices adjacent, if they aren't already. A faster way to recognize duplicates would probably speed things up. @<Private sub...@>= static void adjac(u,v) Vertex *u,*v; {@+Arc *a; for (a=u->arcs;a;a=a->next) if (a->tip==v) return; gb_new_edge(u,v,1L); } @* Bipartite graphs. An even simpler class of Mona-Lisa-based graphs is obtained by considering the |m| rows and |n| columns to be individual vertices, with a row adjacent to a column if the associated pixel value is sufficiently large or sufficiently small. All edges have length~1. The subroutine call |bi_lisa(m,n,m0,m1,n0,n1,thresh,c)| constructs the bipartite graph corresponding to the $m\times n$ digitization produced by |lisa|, using parameters |(m0,m1,n0,n1)| to define a rectangular subpicture as described earlier. The threshold parameter |thresh| should be between 0 and~65535. If the pixel value in row |k| and column |l| is at least |thresh/65535| of its maximum, vertices |k| and~|l| will be adjacent. If |c!=0|, however, the convention is reversed; vertices are then adjacent when the corresponding pixel value is {\sl smaller\/} than |thresh/65535|. Thus adjacencies come from ``light'' areas of da Vinci's painting when |c=0| and from ``dark'' areas when |c!=0|. There are |m+n| vertices and up to $m\times n$ edges. The actual pixel value is recorded in utility field |b.I| of each arc, and scaled to be in the range $[0,65535]$. @p Graph *bi_lisa(m,n,m0,m1,n0,n1,thresh,c) unsigned long m,n; /* number of rows and columns desired */ unsigned long m0,m1; /* input will be from rows $[|m0|\,.\,.\,|m1|)$ */ unsigned long n0,n1; /* and from columns $[|n0|\,.\,.\,|n1|)$ */ unsigned long thresh; /* threshold defining adjacency */ long c; /* should we prefer dark pixels to light pixels? */ {@+@<Local variables for |bi_lisa|@>@;@# init_area(working_storage); @<Set up a bipartite graph with |m+n| vertices@>; @<Put the appropriate edges into the bigraph@>; gb_free(working_storage); if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } return new_graph; } @ @<Local variables for |bi_lisa|@>= Graph *new_graph; /* the graph constructed by |bi_lisa| */ register long k,l; /* all-purpose indices */ Area working_storage; /* tables needed while |bi_lisa| does its thinking */ long *a; /* the matrix constructed by |lisa| */ long *apos; /* the location of |a[k,l]| */ register Vertex *u,*v; /* current vertices of interest */ @ @<Set up a bipartite graph...@>= a=lisa(m,n,65535L,m0,m1,n0,n1,0L,0L,working_storage); if (a==NULL) return NULL; /* |panic_code| has been set by |lisa| */ sscanf(lisa_id,"lisa(%lu,%lu,65535,%lu,%lu,%lu,%lu",&m,&n,&m0,&m1,&n0,&n1); new_graph=gb_new_graph(m+n); if (new_graph==NULL) panic(no_room); /* out of memory before we're even started */ sprintf(new_graph->id,"bi_lisa(%lu,%lu,%lu,%lu,%lu,%lu,%lu,%c)", m,n,m0,m1,n0,n1,thresh,c?'1':'0'); new_graph->util_types[7]='I'; /* enable field |b.I| */ mark_bipartite(new_graph,m); for (k=0,v=new_graph->vertices;k<m;k++,v++) { sprintf(str_buf,"r%ld",k); /* row vertices are called |"r0"|, |"r1"|, etc. */ v->name=gb_save_string(str_buf); } for (l=0;l<n;l++,v++) { sprintf(str_buf,"c%ld",l); /* column vertices are called |"c0"|, |"c1"|, etc. */ v->name=gb_save_string(str_buf); } @ Since we've called |lisa| with |d=65535|, the determination of adjacency is simple. @<Put the appropriate edges into the bigraph@>= for (u=new_graph->vertices,apos=a;u<new_graph->vertices+m;u++) for (v=new_graph->vertices+m;v<new_graph->vertices+m+n;apos++,v++) { if (c?*apos<thresh:*apos>=thresh) { gb_new_edge(u,v,1L); u->arcs->b.I=v->arcs->b.I=*apos; } } @* Index. As usual, we close with an index that shows where the identifiers of \\{gb\_lisa} are defined and used. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gb_miles.w������������������������������������������������������������������������������������������0000444�0001750�0001750�00000040135�07675624273�011174� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_\,MILES} \prerequisites{GB\_\,GRAPH}{GB\_\,IO} @* Introduction. This GraphBase module contains the |miles| subroutine, which creates a family of undirected graphs based on highway mileage data between North American cities. Examples of the use of this procedure can be found in the demo programs {\sc MILES\_\,SPAN} and {\sc GB\_\,PLANE}. @(gb_miles.h@>= extern Graph *miles(); @ The subroutine call {\advance\thinmuskip 0mu plus 4mu |miles(n,north_weight,west_weight,pop_weight,max_distance,max_degree,seed)|} constructs a graph based on the information in \.{miles.dat}. Each vertex of the graph corresponds to one of the 128 cities whose name is alphabetically greater than or equal to `Ravenna, Ohio' in the 1949 edition of Rand McNally {\char`\&} Company's {\sl Standard Highway Mileage Guide}. Edges between vertices are assigned lengths representing distances between cities, in miles. In most cases these mileages come from the Rand McNally Guide, but several dozen entries needed to be changed drastically because they were obviously too large or too small; in such cases an educated guess was made. Furthermore, about 5\% of the entries were adjusted slightly in order to ensure that all distances satisfy the ``triangle inequality'': The graph generated by |miles| has the property that the distance from |u| to~|v| plus the distance from |v| to~|w| always exceeds or equals the distance from |u| to~|w|. The constructed graph will have $\min(n,128)$ vertices; the default value |n=128| is substituted if |n=0|. If |n| is less than 128, the |n| cities will be selected by assigning a weight to each city and choosing the |n| with largest weight, using random numbers to break ties in case of equal weights. Weights are computed by the formula $$ |north_weight|\cdot|lat|+|west_weight|\cdot|lon|+|pop_weight|\cdot|pop|, $$ where |lat| is latitude north of the equator, |lon| is longitude west of Greenwich, and |pop| is the population in 1980. Both |lat| and |lon| are given in ``centidegrees'' (hundredths of degrees). For example, San Francisco has |lat=3778|, |lon=12242|, and |pop=678974|; this means that, before the recent earthquake, it was located at $37.78^\circ$ north latitude and $122.42^\circ$ west longitude, and that it had 678,974 residents in the 1980 census. The weight parameters must satisfy $$ \vert|north_weight|\vert\le100{,}000,\quad \vert|west_weight|\vert\le100{,}000,\quad \vert|pop_weight|\vert\le100.$$ The constructed graph will be ``complete''---that is, it will have edges between every pair of vertices---unless special values are given to the parameters |max_distance| or |max_degree|. If |max_distance!=0|, edges with more than |max_distance| miles will not appear; if |max_degree!=0|, each vertex will be limited to at most |max_degree| of its shortest edges. Vertices of the graph will appear in order of decreasing weight. The |seed| parameter defines the pseudo-random numbers used wherever a ``random'' choice between equal-weight vertices or equal-length edges needs to be made. @d MAX_N 128 @(gb_miles.h@>= #define MAX_N 128 /* maximum and default number of cities */ @ Examples: The call |miles(100,0,0,1,0,0,0)| will construct a complete graph on 100 vertices, representing the 100 most populous cities in the database. It turns out that San Diego, with a population of 875,538, is the winning city by this criterion, followed by San Antonio (population 786,023), San Francisco (678,974), and Washington D.C. (638,432). To get |n| cities in the western United States and Canada, you can say $|miles|(n,0,1,0,\ldots\,)$; to get |n| cities in the Northeast, use a call like $|miles|(n,1,-1,0,\ldots\,)$. A parameter setting like $(50,-500,0,1,\ldots\,)$ produces mostly Southern cities, except for a few large metropolises in the north. If you ask for |miles(n,a,b,c,0,1,0)|, you get an edge between cities if and only if each city is the nearest to the other, among the |n| cities selected. (The graph is always undirected: There is an arc from |u| to~|v| if and only if there's an arc of the same length from |v| to~|u|.) A random selection of cities can be obtained by calling |miles(n,0,0,0,m,d,s)|. Different choices of the seed number |s| will produce different selections, in a system-independent manner; identical results will be obtained on all computers when identical parameters have been specified. Equivalent experiments on algorithms for graph manipulation can therefore be performed by researchers in different parts of the world. Any value of |s| between 0 and $2^{31}-1$ is permissible. @ If the |miles| routine encounters a problem, it returns |NULL| (\.{NULL}), after putting a code number into the external variable |panic_code|. This code number identifies the type of failure. Otherwise |miles| 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_trouble_code=0;@+return NULL;@+} @ The \CEE/ file \.{gb\_miles.c} has the following overall shape: @p #include "gb_io.h" /* we will use the {\sc GB\_\,IO} routines for input */ #include "gb_flip.h" /* we will use the {\sc GB\_\,FLIP} routines for random numbers */ #include "gb_graph.h" /* we will use the {\sc GB\_\,GRAPH} data structures */ #include "gb_sort.h" /* and the linksort routine */ @h@# @<Type declarations@>@; @<Private variables@>@; @# Graph *miles(n,north_weight,west_weight,pop_weight, max_distance,max_degree,seed) unsigned long n; /* number of vertices desired */ long north_weight; /* coefficient of latitude in the weight function */ long west_weight; /* coefficient of longitude in the weight function */ long pop_weight; /* coefficient of population in the weight function */ unsigned long max_distance; /* maximum distance in an edge, if nonzero */ unsigned long max_degree; /* maximum number of edges per vertex, if nonzero */ long seed; /* random number seed */ {@+@<Local variables@>@;@# gb_init_rand(seed); @<Check that the parameters are valid@>; @<Set up a graph with |n| vertices@>; @<Read the data file \.{miles.dat} and compute city weights@>; @<Determine the |n| cities to use in the graph@>; @<Put the appropriate edges into the graph@>; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } return new_graph; } @ @<Local var...@>= Graph *new_graph; /* the graph constructed by |miles| */ register long j,k; /* all-purpose indices */ @ @<Check that the parameters are valid@>= if (n==0 || n>MAX_N) n=MAX_N; if (max_degree==0 || max_degree>=n) max_degree=n-1; if (north_weight>100000 || west_weight>100000 || pop_weight>100 @| || north_weight<-100000 || west_weight<-100000 || pop_weight<-100) panic(bad_specs); /* the magnitude of at least one weight is too big */ @ @<Set up a graph with |n| vertices@>= 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,"miles(%lu,%ld,%ld,%ld,%lu,%lu,%ld)", n,north_weight,west_weight,pop_weight,max_distance,max_degree,seed); strcpy(new_graph->util_types,"ZZIIIIZZZZZZZZ"); @* Vertices. As we read in the data, we construct a list of nodes, each of which contains a city's name, latitude, longitude, population, and weight. These nodes conform to the specifications stipulated in the {\sc GB\_\,SORT} module. After the list has been sorted by weight, the top |n| entries will be the vertices of the new graph. @<Type decl...@>= typedef struct node_struct { /* records to be sorted by |gb_linksort| */ long key; /* the nonnegative sort key (weight plus $2^{30}$) */ struct node_struct *link; /* pointer to next record */ long kk; /* index of city in the original database */ long lat,lon,pop; /* latitude, longitude, population */ char name[30]; /* |"City Name, ST"| */ } node; @ The constants defined here are taken from the specific data in \.{miles.dat}, because this routine is not intended to be perfectly general. @<Private...@>= static long min_lat=2672, max_lat=5042, min_lon=7180, max_lon=12312, min_pop=2521, max_pop=875538; /* tight bounds on data entries */ static node *node_block; /* array of nodes holding city info */ static long *distance; /* array of distances */ @ The data in \.{miles.dat} appears in 128 groups of lines, one for each city, in reverse alphabetical order. These groups have the general form $$\vcenter{\halign{\tt#\hfil\cr City Name, ST[lat,lon]pop\cr d1 d2 d3 d4 d5 d6 ... (possibly several lines' worth)\cr }}$$ where \.{City Name} is the name of the city (possibly including spaces); \.{ST} is the two-letter state code; \.{lat} and \.{lon} are latitude and longitude in hundredths of degrees; \.{pop} is the population; and the remaining numbers \.{d1}, \.{d2}, \dots\ are distances to the previously named cities in reverse order. Each distance is separated from the previous item by either a blank space or a newline character. For example, the line $$\hbox{\tt San Francisco, CA[3778,12242]678974}$$ specifies the data about San Francisco that was mentioned earlier. From the first few groups $$\vcenter{\halign{\tt#\hfil\cr Youngstown, OH[4110,8065]115436\cr Yankton, SD[4288,9739]12011\cr 966\cr Yakima, WA[4660,12051]49826\cr 1513 2410\cr Worcester, MA[4227,7180]161799\cr 2964 1520 604\cr }}$$ we learn that the distance from Worcester, Massachusetts, to Yakima, Washington, is 2964 miles; from Worcester to Youngstown it is 604 miles. The following two-letter ``state codes'' are used for Canadian provinces: $\.{BC}=\null$British Columbia, $\.{MB}=\null$Manitoba, $\.{ON}=\null$Ontario, $\.{SK}=\null$Saskatchewan. @<Read the data file \.{miles.dat} and compute city weights@>= node_block=gb_typed_alloc(MAX_N,node,new_graph->aux_data); distance=gb_typed_alloc(MAX_N*MAX_N,long,new_graph->aux_data); if (gb_trouble_code) { gb_free(new_graph->aux_data); panic(no_room+1); /* no room to copy the data */ } if (gb_open("miles.dat")!=0) panic(early_data_fault); /* couldn't open |"miles.dat"| using GraphBase conventions; |io_errors| tells why */ for (k=MAX_N-1; k>=0; k--) @<Read and store data for city |k|@>; if (gb_close()!=0) panic(late_data_fault); /* something's wrong with |"miles.dat"|; see |io_errors| */ @ The bounds we've imposed on |north_weight|, |west_weight|, and |pop_weight| guarantee that the key value computed here will be between 0 and~$2^{31}$. @<Read and store...@>= {@+register node *p; p=node_block+k; p->kk=k; if (k) p->link=p-1; gb_string(p->name,'['); if (gb_char()!='[') panic(syntax_error); /* out of sync in \.{miles.dat} */ p->lat=gb_number(10); if (p->lat<min_lat || p->lat>max_lat || gb_char()!=',') panic(syntax_error+1); /* latitude data was clobbered */ p->lon=gb_number(10); if (p->lon<min_lon || p->lon>max_lon || gb_char()!=']') panic(syntax_error+2); /* longitude data was clobbered */ p->pop=gb_number(10); if (p->pop<min_pop || p->pop>max_pop) panic(syntax_error+3); /* population data was clobbered */ p->key=north_weight*(p->lat-min_lat) +west_weight*(p->lon-min_lon) +pop_weight*(p->pop-min_pop)+0x40000000; @<Read the mileage data for city |k|@>; gb_newline(); } @ @d d(j,k) *(distance+(MAX_N*j+k)) @<Read the mileage...@>= { for (j=k+1; j<MAX_N; j++) { if (gb_char()!=' ') gb_newline(); d(j,k)=d(k,j)=gb_number(10); } } @ Once all the nodes have been set up, we can use the |gb_linksort| routine to sort them into the desired order. This routine, which is part of the \\{GB\_\,SORT} module, builds 128 lists from which the desired nodes are readily accessed in decreasing order of weight, using random numbers to break ties. We set the population to zero in every city that isn't chosen. Then that city will be excluded when edges are examined later. @<Determine the |n| cities to use in the graph@>= {@+register node *p; /* the current node being considered */ register Vertex *v=new_graph->vertices; /* the first unfilled vertex */ gb_linksort(node_block+MAX_N-1); for (j=127; j>=0; j--) for (p=(node*)gb_sorted[j]; p; p=p->link) { if (v<new_graph->vertices+n) @<Add city |p->kk| to the graph@>@; else p->pop=0; /* this city is not being used */ } } @ Utility fields |x| and |y| for each vertex are set to coordinates that can be used in geometric computations; these coordinates are obtained by simple linear transformations of latitude and longitude (not by any kind of sophisticated polyconic projection). We will have $$0\le x\le5132, \qquad 0\le y\le 3555.$$ Utility field~|z| is set to the city's index number (0 to 127) in the original database. Utility field~|w| is set to the city's population. The coordinates computed here are compatible with those in the \TEX/ file \.{cities.texmap}. Users might want to incorporate edited copies of that file into documents that display results obtained with |miles| graphs. @.cities.texmap@> @d x_coord x.I @d y_coord y.I @d index_no z.I @d people w.I @<Add city |p->kk| to the graph@>= { v->x_coord=max_lon-p->lon; /* |x| coordinate is complement of longitude */ v->y_coord=p->lat-min_lat; v->y_coord+=(v->y_coord)>>1; /* |y| coordinate is 1.5 times latitude */ v->index_no=p->kk; v->people=p->pop; v->name=gb_save_string(p->name); v++; } @ @(gb_miles.h@>= #define x_coord @t\quad@> x.I /* utility field definitions for the header file */ #define y_coord @t\quad@> y.I #define index_no @t\quad@> z.I #define people @t\quad@> w.I @* Arcs. We make the distance negative in the matrix entry for an arc that is not to be included. Nothing needs to be done in this regard unless the user has specified a maximum degree or a maximum edge length. @<Put the approp...@>= if (max_distance>0 || max_degree>0) @<Prune unwanted edges by negating their distances@>; {@+register Vertex *u,*v; for (u=new_graph->vertices;u<new_graph->vertices+n;u++) { j=u->index_no; for (v=u+1;v<new_graph->vertices+n;v++) { k=v->index_no; if (d(j,k)>0 && d(k,j)>0) gb_new_edge(u,v,d(j,k)); } } } @ @<Prune...@>= {@+register node *p; if (max_degree==0) max_degree=MAX_N; if (max_distance==0) max_distance=30000; for (p=node_block; p<node_block+MAX_N; p++) if (p->pop) { /* this city not deleted */ k=p->kk; @<Blank out all undesired edges from city |k|@>; } } @ Here we reuse the key fields of the nodes, storing complementary distances there instead of weights. We also let the sorting routine change the link fields. The other fields, however---especially |pop|---remain unchanged. Yes, the author knows this is a wee bit tricky, but why~not? @<Blank...@>= {@+register node *q; register node*s=NULL; /* list of nodes containing edges from city |k| */ for (q=node_block; q<node_block+MAX_N; q++) if (q->pop && q!=p) { /* another city not deleted */ j=d(k,q->kk); /* distance from |p| to |q| */ if (j>max_distance) d(k,q->kk)=-j; else { q->key=max_distance-j; q->link=s; s=q; } } gb_linksort(s); /* now all the surviving edges from |p| are in the list |gb_sorted[0]| */ j=0; /* |j| counts how many edges have been accepted */ for (q=(node*)gb_sorted[0]; q; q=q->link) if (++j>max_degree) d(k,q->kk)=-d(k,q->kk); } @ Random access to the distance matrix is provided to users via the external function |miles_distance|. Caution: This function can be used only with the graph most recently made by |miles|, and only when the graph's |aux_data| has not been recycled, and only when the |z| utility fields have not been used for another purpose. The result might be negative when an edge has been suppressed. Moreover, we can in fact have |miles_distance(u,v)<0| when |miles_distance(v,u)>0|, if the distance in question was suppressed by the |max_degree| constraint on~|u| but not on~|v|. @p long miles_distance(u,v) Vertex *u,*v; { return d(u->index_no,v->index_no); } @ @(gb_miles.h@>= extern long miles_distance(); @* Index. As usual, we close with an index that shows where the identifiers of \\{gb\_miles} are defined and used. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gb_plane.w������������������������������������������������������������������������������������������0000444�0001750�0001750�00000115762�07224007074�011153� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_\,PLANE} \prerequisite{GB\_\,MILES} @* Introduction. This GraphBase module contains the |plane| subroutine, which constructs undirected planar graphs from vertices located randomly in a rectangle, as well as the |plane_miles| routine, which constructs planar graphs based on the mileage and coordinate data in \.{miles.dat}. Both routines use a general-purpose |delaunay| subroutine, which computes the Delaunay triangulation of a given set of points. @d plane_miles p_miles /* abbreviation for Procrustean external linkage */ @(gb_plane.h@>= #define plane_miles p_miles extern Graph *plane(); extern Graph *plane_miles(); extern void delaunay(); @ The subroutine call |plane(n,x_range,y_range,extend,prob,seed)| constructs a planar graph whose vertices have integer coordinates uniformly distributed in the rectangle $$\{\,(x,y)\;\mid\;0\le x<|x_range|, \;0\le y<|y_range|\,\}\,.$$ The values of |x_range| and |y_range| must be at most $2^{14}=16384$; the latter value is the default, which is substituted if |x_range| or |y_range| is given as zero. If |extend==0|, the graph will have |n| vertices; otherwise it will have |n+1| vertices, where the |(n+1)|st is assigned the coordinates $(-1,-1)$ and may be regarded as a point at~$\infty$. Some of the |n|~finite vertices might have identical coordinates, particularly if the point density |n/(x_range*y_range)| is not very small. The subroutine works by first constructing the Delaunay triangulation of the points, then discarding each edge of the resulting graph with probability |prob/65536|. Thus, for example, if |prob| is zero the full Delaunay triangulation will be returned; if |prob==32768|, about half of the Delaunay edges will remain. Each finite edge is assigned a length equal to the Euclidean distance between points, multiplied by $2^{10}$ and rounded to the nearest integer. If |extend!=0|, the Delaunay triangulation will also contain edges between $\infty$ and all points of the convex hull; such edges, if not discarded, are assigned length $2^{28}$, otherwise known as |INFTY|. If |extend!=0| and |prob==0|, the graph will have $n+1$ vertices and $3(n-1)$ edges; this is the maximum number of edges that a planar graph on $n+1$ vertices can have. In such a case the average degree of a vertex will be $6(n-1)/(n+1)$, slightly less than~6; hence, if |prob==32768|, the average degree of a vertex will usually be near~3. As with all other GraphBase routines that rely on random numbers, different values of |seed| will produce different graphs, in a machine-independent fashion that is reproducible on many different computers. Any |seed| value between 0 and $2^{31}-1$ is permissible. @d INFTY 0x10000000L /* ``infinite'' length */ @(gb_plane.h@>= #define INFTY @t\quad@> 0x10000000L @ If the |plane| routine encounters a problem, it returns |NULL| (\.{NULL}), after putting a code number into the external variable |panic_code|. This code number identifies the type of failure. Otherwise |plane| 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_trouble_code=0;@+return NULL;@+} @ Here is the overall shape of the \CEE/ file \.{gb\_plane.c}\kern.2em: @p #include "gb_flip.h" /* we will use the {\sc GB\_\,FLIP} routines for random numbers */ #include "gb_graph.h" /* we will use the {\sc GB\_\,GRAPH} data structures */ #include "gb_miles.h" /* and we might use {\sc GB\_\,MILES} for mileage data */ #include "gb_io.h" /* and {\sc GB\_\,MILES} uses {\sc GB\_\,IO}, which has |str_buf| */ @h@# @<Type declarations@>@; @<Global variables@>@; @<Subroutines for arithmetic@>@; @<Other subroutines@>@; @<The |delaunay| routine@>@; @<The |plane| routine@>@; @<The |plane_miles| routine@>@; @ @<The |plane| routine@>= Graph *plane(n,x_range,y_range,extend,prob,seed) unsigned long n; /* number of vertices desired */ unsigned long x_range,y_range; /* upper bounds on rectangular coordinates */ unsigned long extend; /* should a point at infinity be included? */ unsigned long prob; /* probability of rejecting a Delaunay edge */ long seed; /* random number seed */ {@+Graph *new_graph; /* the graph constructed by |plane| */ register Vertex *v; /* the current vertex of interest */ register long k; /* the canonical all-purpose index */ gb_init_rand(seed); if (x_range>16384 || y_range>16384) panic(bad_specs); /* range too large */ if (n<2) panic(very_bad_specs); /* don't make |n| so small, you fool */ if (x_range==0) x_range=16384; /* default */ if (y_range==0) y_range=16384; /* default */ @<Set up a graph with |n| uniformly distributed vertices@>; @<Compute the Delaunay triangulation and run through the Delaunay edges; reject them with probability |prob/65536|, otherwise append them with their Euclidean length@>; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } if (extend) new_graph->n++; /* make the ``infinite'' vertex legitimate */ return new_graph; } @ The coordinates are placed into utility fields |x_coord| and |y_coord|. A random ID number is also stored in utility field~|z_coord|; this number is used by the |delaunay| subroutine to break ties when points are equal or collinear or cocircular. No two vertices have the same ID number. (The header file \.{gb\_miles.h} defines |x_coord|, |y_coord|, and |index_no| to be |x.I|, |y.I|, and |z.I| respectively.) @d z_coord z.I @<Set up a graph with |n| uniform...@>= if (extend) extra_n++; /* allocate one more vertex than usual */ 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,"plane(%lu,%lu,%lu,%lu,%lu,%ld)", n,x_range,y_range,extend,prob,seed); strcpy(new_graph->util_types,"ZZZIIIZZZZZZZZ"); for (k=0,v=new_graph->vertices; k<n; k++,v++) { v->x_coord=gb_unif_rand(x_range); v->y_coord=gb_unif_rand(y_range); v->z_coord=((long)(gb_next_rand()/n))*n+k; sprintf(str_buf,"%ld",k);@+v->name=gb_save_string(str_buf); } if (extend) { v->name=gb_save_string("INF"); v->x_coord=v->y_coord=v->z_coord=-1; extra_n--; } @ @(gb_plane.h@>= #define x_coord @t\quad@> x.I #define y_coord @t\quad@> y.I #define z_coord @t\quad@> z.I @* Delaunay triangulation. The Delaunay triangulation of a set of vertices in the plane consists of all line segments $uv$ such that there exists a circle passing through $u$ and~$v$ containing no other vertices. Equivalently, $uv$ is a Delaunay edge if and only if the Voronoi regions for $u$ and $v$ are adjacent; the Voronoi region of a vertex~$u$ is the polygon with the property that all points inside it are closer to $u$ than to any other vertex. In this sense, we can say that Delaunay edges connect vertices with their ``neighbors.'' @^Delaunay [Delone], Boris Nikolaevich@> The definitions in the previous paragraph assume that no two vertices are equal, that no three vertices lie on a straight line, and that no four vertices lie on a circle. If those nondegeneracy conditions aren't satisfied, we can perturb the points very slightly so that the assumptions do hold. Another way to characterize the Delaunay triangulation is to consider what happens when we map a given set of points onto the unit sphere via stereographic projection: Point $(x,y)$ is mapped to $$\bigl(2x/(r^2+1),2y/(r^2+1),(r^2-1)/(r^2+1)\bigr)\,,$$ where $r^2=x^2+y^2$. If we now extend the configuration by adding $(0,0,1)$, which is the limiting point on the sphere when $r$ approaches infinity, the Delaunay edges of the original points turn out to be edges of the polytope defined by the mapped points. This polytope, which is the 3-dimensional convex hull of $n+1$ points on the sphere, also has edges from $(0,0,1)$ to the mapped points that correspond to the 2-dimensional convex hull of the original points. Under our assumption of nondegeneracy, the faces of this polytope are all triangles; hence its edges are said to form a triangulation. A self-contained presentation of all the relevant theory, together with an exposition and proof of correctness of the algorithm below, can be found in the author's monograph {\sl Axioms and Hulls}, Lecture Notes in Computer Science {\bf606} (Springer-Verlag, 1992). @^Axioms and Hulls@> For further references, see Franz Aurenhammer, {\sl ACM Computing Surveys\/ @^Aurenhammer, Franz@> \bf23} (1991), 345--405. @ The |delaunay| procedure, which finds the Delaunay triangulation of a given set of vertices, is the key ingredient in \\{gb\_plane}'s algorithms for generating planar graphs. The given vertices should appear in a GraphBase graph~|g| whose edges, if any, are ignored by |delaunay|. The coordinates of each vertex appear in utility fields |x_coord| and~|y_coord|, which must be nonnegative and less than $2^{14}=16384$. The utility field~|z_coord| must contain a unique ID number, distinct for every vertex, so that the algorithm can break ties in cases of degeneracy. (Note: These assumptions about the input data are the responsibility of the calling procedure; |delaunay| does not double-check them. If they are violated, catastrophic failure is possible.) Instead of returning the Delaunay triangulation as a graph, |delaunay| communicates its answer implicitly by performing the procedure call |f(u,v)| on every pair of vertices |u| and~|v| joined by a Delaunay edge. Here |f|~is a procedure supplied as a parameter; |u| and~|v| are either pointers to vertices or |NULL| (i.e., \.{NULL}), where |NULL| denotes the vertex ``$\infty$.'' As remarked above, edges run between $\infty$ and all vertices on the convex hull of the given points. The graph of all edges, including the infinite edges, is planar. For example, if the vertex at infinity is being ignored, the user can declare $$\vcenter{\halign{#\hfil\cr |void ins_finite(u,v)|\cr \qquad|Vertex *u,*v;|\cr |{@+if (u&&v)@+gb_new_edge(u,v,1L);@+}|\cr}}$$ Then the procedure call |delaunay(g,ins_finite)| will add all the finite Delaunay edges to the current graph~|g|, giving them all length~1. If |delaunay| is unable to allocate enough storage to do its work, it will set |gb_trouble_code| nonzero and there will be no edges in the triangulation. @<The |delaunay| routine@>= void delaunay(g,f) Graph *g; /* vertices in the plane */ void @[@] (*f)(); /* procedure that absorbs the triangulated edges */ {@+@<Local variables for |delaunay|@>;@# @<Find the Delaunay triangulation of |g|, or return with |gb_trouble_code| nonzero if out of memory@>; @<Call |f(u,v)| for each Delaunay edge \\{uv}@>; gb_free(working_storage); } @ The procedure passed to |delaunay| will communicate with |plane| via global variables called |gprob| and |inf_vertex|. @<Glob...@>= static unsigned long gprob; /* copy of the |prob| parameter */ static Vertex *inf_vertex; /* pointer to the vertex $\infty$, or |NULL| */ @ @<Compute the Delaunay triangulation and run through the Delaunay edges; reject them with probability |prob/65536|, otherwise append them with their Euclidean length@>= gprob=prob; if (extend) inf_vertex=new_graph->vertices+n; else inf_vertex=NULL; delaunay(new_graph,new_euclid_edge); @ @<Other...@>= static void new_euclid_edge(u,v) Vertex *u,*v; {@+register long dx,dy; if ((gb_next_rand()>>15)>=gprob) { if (u) { if (v) { dx=u->x_coord-v->x_coord; dy=u->y_coord-v->y_coord; gb_new_edge(u,v,int_sqrt(dx*dx+dy*dy)); }@+else if (inf_vertex) gb_new_edge(u,inf_vertex,INFTY); }@+else if (inf_vertex) gb_new_edge(inf_vertex,v,INFTY); } } @* Arithmetic. Before we lunge into the world of geometric algorithms, let's build up some confidence by polishing off some subroutines that will be needed to ensure correct results. We assume that |long| integers are less than $2^{31}$. First is a routine to calculate $s=\lfloor2^{10}\sqrt x+{1\over2}\rfloor$, the nearest integer to $2^{10}$ times the square root of a given nonnegative integer~|x|. If |x>0|, this is the unique integer such that $2^{20}x-s\le s^2<2^{20}x+s$. The following routine appears to work by magic, but the mystery goes away when one considers the invariant relations $$ m=\lfloor 2^{2k-21}\rfloor,\qquad 0<y=\lfloor 2^{20-2k}x\rfloor-s^2+s\le q=2s.$$ (Exception: We might actually have $y=0$ for a short time when |q=2|.) @<Subroutines for arith...@>= static long int_sqrt(x) long x; {@+register long y, m, q=2; long k; if (x<=0) return 0; for (k=25,m=0x20000000;x<m;k--,m>>=2) ; /* find the range */ if (x>=m+m) y=1; else y=0; do @<Decrease |k| by 1, maintaining the invariant relations between |x|, |y|, |m|, and |q|@>@; while (k); return q>>1; } @ @<Decrease |k| by 1, maintaining the invariant relations...@>= { if (x&m) y+=y+1; else y+=y; m>>=1; if (x&m) y+=y-q+1; else y+=y-q; q+=q; if (y>q) y-=q,q+=2; else if (y<=0) q-=2,y+=q; m>>=1; k--; } @ We are going to need multiple-precision arithmetic in order to calculate certain geometric predicates properly, but it turns out that we do not need to implement general-purpose subroutines for bignums. It suffices to have a single special-purpose routine called $|sign_test|(|x1|,|x2|,|x3|,\allowbreak|y1|,|y2|,|y3|)$, which computes a single-precision integer having the same sign as the dot product $$\hbox{|x1*y1+x2*y2+x3*y3|}$$ when we have $-2^{29}<|x1|,|x2|,|x3|<2^{29}$ and $0\le|y1|,|y2|,|y3|<2^{29}$. @<Subroutines for arith...@>= static long sign_test(x1,x2,x3,y1,y2,y3) long x1,x2,x3,y1,y2,y3; {@+long s1,s2,s3; /* signs of individual terms */ long a,b,c; /* components of a redundant representation of the dot product */ register long t; /* temporary register for swapping */ @<Determine the signs of the terms@>; @<If the answer is obvious, return it without further ado; otherwise, arrange things so that |x3*y3| has the opposite sign to |x1*y1+x2*y2|@>; @<Compute a redundant representation of |x1*y1+x2*y2+x3*y3|@>; @<Return the sign of the redundant representation@>; } @ @<Determine the signs of the terms@>= if (x1==0 || y1==0) s1=0; else { if (x1>0) s1=1; else x1=-x1,s1=-1; } if (x2==0 || y2==0) s2=0; else { if (x2>0) s2=1; else x2=-x2,s2=-1; } if (x3==0 || y3==0) s3=0; else { if (x3>0) s3=1; else x3=-x3,s3=-1; } @ The answer is obvious unless one of the terms is positive and one of the terms is negative. @<If the answer is obvious, return it without further ado; otherwise, arrange things so that |x3*y3| has the opposite sign to |x1*y1+x2*y2|@>= if ((s1>=0 && s2>=0 && s3>=0) || (s1<=0 && s2<=0 && s3<=0)) return (s1+s2+s3); if (s3==0 || s3==s1) { t=s3;@+s3=s2;@+s2=t; t=x3;@+x3=x2;@+x2=t; t=y3;@+y3=y2;@+y2=t; }@+else if (s3==s2) { t=s3;@+s3=s1;@+s1=t; t=x3;@+x3=x1;@+x1=t; t=y3;@+y3=y1;@+y1=t; } @ We make use of a redundant representation $2^{28}a+2^{14}b+c$, which can be computed by brute force. (Everything is understood to be multiplied by |-s3|.) @<Compute a redundant...@>= {@+register long lx,rx,ly,ry; lx=x1/0x4000;@+rx=x1%0x4000; /* split off the least significant 14 bits */ ly=y1/0x4000;@+ry=y1%0x4000; a=lx*ly;@+b=lx*ry+ly*rx;@+c=rx*ry; lx=x2/0x4000;@+rx=x2%0x4000; ly=y2/0x4000;@+ry=y2%0x4000; a+=lx*ly;@+b+=lx*ry+ly*rx;@+c+=rx*ry; lx=x3/0x4000;@+rx=x3%0x4000; ly=y3/0x4000;@+ry=y3%0x4000; a-=lx*ly;@+b-=lx*ry+ly*rx;@+c-=rx*ry; } @ Here we use the fact that $\vert c\vert<2^{29}$. @<Return the sign...@>= if (a==0) goto ez; if (a<0) a=-a,b=-b,c=-c,s3=-s3; while (c<0) { a--;@+c+=0x10000000; if (a==0) goto ez; } if (b>=0) return -s3; /* the answer is clear when |a>0 && b>=0 && c>=0| */ b=-b; a-=b/0x4000; if (a>0) return -s3; if (a<=-2) return s3; return -s3*((a*0x4000-b%0x4000)*0x4000+c); ez:@+ if (b>=0x8000) return -s3; if (b<=-0x8000) return s3; return -s3*(b*0x4000+c); @*Determinants. The |delaunay| routine bases all of its decisions on two geometric predicates, which depend on whether certain determinants are positive or negative. The first predicate, |ccw(u,v,w)|, is true if and only if the three points $(u,v,w)$ have a counterclockwise orientation. This means that if we draw the unique circle through those points, and if we travel along that circle in the counterclockwise direction starting at~|u|, we will encounter |v| before~|w|. It turns out that |ccw(u,v,w)| holds if and only if the determinant $$\left\vert\matrix{x_u&y_u&1\cr x_v&y_v&1\cr x_w&y_w&1\cr} \right\vert=\left\vert\matrix{x_u-x_w&y_u-y_w\cr x_v-x_w&y_v-y_w\cr} \right\vert$$ is positive. The evaluation must be exact; if the answer is zero, a special tie-breaking rule must be used because the three points were collinear. The tie-breaking rule is tricky (and necessarily so, according to the theory in {\sl Axioms and Hulls\/}). Integer evaluation of that determinant will not cause |long| integer overflow, because we have assumed that all |x| and |y| coordinates lie between 0 and~$2^{14}-1$, inclusive. In fact, we could go up to $2^{15}-1$ without risking overflow; but the limitation to 14 bits will be helpful when we consider a more complicated determinant below. @<Other...@>= static long ccw(u,v,w) Vertex *u,*v,*w; {@+register long wx=w->x_coord, wy=w->y_coord; /* $x_w$, $y_w$ */ register long det=(u->x_coord-wx)*(v->y_coord-wy) -(u->y_coord-wy)*(v->x_coord-wx); Vertex *t; if (det==0) { det=1; if (u->z_coord>v->z_coord) { t=u;@+u=v;@+v=t;@+det=-det; } if (v->z_coord>w->z_coord) { t=v;@+v=w;@+w=t;@+det=-det; } if (u->z_coord>v->z_coord) { t=u;@+u=v;@+v=t;@+det=-det; } if (u->x_coord>v->x_coord || (u->x_coord==v->x_coord &&@| (u->y_coord>v->y_coord || (u->y_coord==v->y_coord &&@| (w->x_coord>u->x_coord || (w->x_coord==u->x_coord && w->y_coord>=u->y_coord)))))) det=-det; } return (det>0); } @ The other geometric predicate, |incircle(t,u,v,w)|, is true if and only if point |t| lies outside the circle passing through |u|, |v|, and~|w|, assuming that |ccw(u,v,w)| holds. This predicate makes us work harder, because it is equivalent to the sign of a $4\times4$ determinant that requires twice as much precision: $$\left\vert\matrix{x_t&y_t&x_t^2+y_t^2&1\cr x_u&y_u&x_u^2+y_u^2&1\cr x_v&y_v&x_v^2+y_v^2&1\cr x_w&y_w&x_w^2+y_w^2&1\cr}\right\vert= \left\vert\matrix{x_t-x_w&y_t-y_w&(x_t-x_w)^2+(y_t-y_w)^2\cr x_u-x_w&y_u-y_w&(x_u-x_w)^2+(y_u-y_w)^2\cr x_v-x_w&y_v-y_w&(x_v-x_w)^2+(y_v-y_w)^2\cr} \right\vert\,.$$ The sign can, however, be deduced by the |sign_test| subroutine we had the foresight to provide earlier. @<Other...@>= static long incircle(t,u,v,w) Vertex *t,*u,*v,*w; {@+register long wx=w->x_coord, wy=w->y_coord; /* $x_w$, $y_w$ */ long tx=t->x_coord-wx, ty=t->y_coord-wy; /* $x_t-x_w$, $y_t-y_w$ */ long ux=u->x_coord-wx, uy=u->y_coord-wy; /* $x_u-x_w$, $y_u-y_w$ */ long vx=v->x_coord-wx, vy=v->y_coord-wy; /* $x_v-x_w$, $y_v-y_w$ */ register long det=sign_test(tx*uy-ty*ux,ux*vy-uy*vx,vx*ty-vy*tx,@| vx*vx+vy*vy,tx*tx+ty*ty,ux*ux+uy*uy); Vertex *s; if (det==0) { @<Sort |(t,u,v,w)| by ID number@>; @<Remove incircle degeneracy@>; } return (det>0); } @ @<Sort...@>= det=1; if (t->z_coord>u->z_coord) { s=t;@+t=u;@+u=s;@+det=-det; } if (v->z_coord>w->z_coord) { s=v;@+v=w;@+w=s;@+det=-det; } if (t->z_coord>v->z_coord) { s=t;@+t=v;@+v=s;@+det=-det; } if (u->z_coord>w->z_coord) { s=u;@+u=w;@+w=s;@+det=-det; } if (u->z_coord>v->z_coord) { s=u;@+u=v;@+v=s;@+det=-det; } @ By slightly perturbing the points, we can always make them nondegenerate, although the details are complicated. A sequence of 12 steps, involving up to four auxiliary functions $$\openup3\jot \eqalign{|ff|(t,u,v,w)&=\left\vert \matrix{x_t-x_v&(x_t-x_w)^2+(y_t-y_w)^2-(x_v-x_w)^2-(y_v-y_w)^2\cr x_u-x_v&(x_u-x_w)^2+(y_u-y_w)^2-(x_v-x_w)^2-(y_v-y_w)^2\cr} \right\vert\,,\cr |gg|(t,u,v,w)&=\left\vert \matrix{y_t-y_v&(x_t-x_w)^2+(y_t-y_w)^2-(x_v-x_w)^2-(y_v-y_w)^2\cr y_u-y_v&(x_u-x_w)^2+(y_u-y_w)^2-(x_v-x_w)^2-(y_v-y_w)^2\cr} \right\vert\,,\cr |hh|(t,u,v,w)&=(x_u-x_t)(y_v-y_w)\,,\cr |jj|(t,u,v,w)&=(x_u-x_v)^2+(y_u-y_w)^2-(x_t-x_v)^2-(y_t-y_w)^2\,,\cr} $$ does the trick, as explained in {\sl Axioms and Hulls}. @<Remove incircle degeneracy@>= {@+long dd; if ((dd=ff(t,u,v,w))<0 || (dd==0 &&@| ((dd=gg(t,u,v,w))<0 || (dd==0 &&@| ((dd=ff(u,t,w,v))<0 || (dd==0 &&@| ((dd=gg(u,t,w,v))<0 || (dd==0 &&@| ((dd=ff(v,w,t,u))<0 || (dd==0 &&@| ((dd=gg(v,w,t,u))<0 || (dd==0 &&@| ((dd=hh(t,u,v,w))<0 || (dd==0 &&@| ((dd=jj(t,u,v,w))<0 || (dd==0 &&@| ((dd=hh(v,t,u,w))<0 || (dd==0 &&@| ((dd=jj(v,t,u,w))<0 || (dd==0 && jj(t,w,u,v)<0)))))))))))))))))))) det=-det; } @ @<Subroutines for arith...@>= static long ff(t,u,v,w) Vertex *t,*u,*v,*w; {@+register long wx=w->x_coord, wy=w->y_coord; /* $x_w$, $y_w$ */ long tx=t->x_coord-wx, ty=t->y_coord-wy; /* $x_t-x_w$, $y_t-y_w$ */ long ux=u->x_coord-wx, uy=u->y_coord-wy; /* $x_u-x_w$, $y_u-y_w$ */ long vx=v->x_coord-wx, vy=v->y_coord-wy; /* $x_v-x_w$, $y_v-y_w$ */ return sign_test(ux-tx,vx-ux,tx-vx,vx*vx+vy*vy,tx*tx+ty*ty,ux*ux+uy*uy); } static long gg(t,u,v,w) Vertex *t,*u,*v,*w; {@+register long wx=w->x_coord, wy=w->y_coord; /* $x_w$, $y_w$ */ long tx=t->x_coord-wx, ty=t->y_coord-wy; /* $x_t-x_w$, $y_t-y_w$ */ long ux=u->x_coord-wx, uy=u->y_coord-wy; /* $x_u-x_w$, $y_u-y_w$ */ long vx=v->x_coord-wx, vy=v->y_coord-wy; /* $x_v-x_w$, $y_v-y_w$ */ return sign_test(uy-ty,vy-uy,ty-vy,vx*vx+vy*vy,tx*tx+ty*ty,ux*ux+uy*uy); } static long hh(t,u,v,w) Vertex *t,*u,*v,*w; { return (u->x_coord-t->x_coord)*(v->y_coord-w->y_coord); } static long jj(t,u,v,w) Vertex *t,*u,*v,*w; {@+register long vx=v->x_coord, wy=w->y_coord; return (u->x_coord-vx)*(u->x_coord-vx)+(u->y_coord-wy)*(u->y_coord-wy)@| -(t->x_coord-vx)*(t->x_coord-vx)-(t->y_coord-wy)*(t->y_coord-wy); } @* Delaunay data structures. Now we have the primitive predicates we need, and we can get on with the geometric aspects of |delaunay|. As mentioned above, each vertex is represented by two coordinates and an ID number, stored in the utility fields |x_coord|, |y_coord|, and~|z_coord|. Each edge of the current triangulation is represented by two arcs pointing in opposite directions; the two arcs are called {\sl mates}. Each arc conceptually has a triangle on its left and a mate on its right. An \&{arc} record differs from an |Arc|; it has three fields: \smallskip |vert| is the vertex this arc leads to, or |NULL| if that vertex is $\infty$; \smallskip |next| is the next arc having the same triangle at the left; \smallskip |inst| is the branch node that points to the triangle at the left, as explained below. \smallskip\noindent If |p| points to an arc, then |p->next->next->next==p|, because a triangle is bounded by three arcs. We also have |p->next->inst==p->inst|, for all arcs~|p|. @<Type...@>= typedef struct a_struct { Vertex *vert; /* |v|, if this arc goes from |u| to |v| */ struct a_struct *next; /* the arc from |v| that shares a triangle with this one */ struct n_struct *inst; /* instruction to change when the triangle is modified */ } arc; @ Storage is allocated in such a way that, if |p| and |q| point respectively to an arc and its mate, then |p+q=&arc_block[0]+&arc_block[m-1]|, where |m| is the total number of arc records allocated in the |arc_block| array. This convention saves us one pointer field in each arc. When setting |q| to the mate of |p|, we need to do the calculation cautiously using an auxiliary register, because the constant |&arc_block[0]+&arc_block[m-1]| might be too large to evaluate without integer overflow on some systems. @^pointer hacks@> @d mate(a,b) { /* given |a|, set |b| to its mate */ reg=max_arc-(siz_t)a; b=(arc*)(reg+min_arc); } @<Local variables for |delaunay|@>= register siz_t reg; /* used while computing mates */ siz_t min_arc,max_arc; /* |&arc_block[0]|, |&arc_block[m-1]| */ arc *next_arc; /* the first arc record that hasn't yet been used */ @ @<Initialize the array of arcs@>= next_arc=gb_typed_alloc(6*g->n-6,arc,working_storage); if (next_arc==NULL) return; /* |gb_trouble_code| is nonzero */ min_arc=(siz_t)next_arc; max_arc=(siz_t)(next_arc+(6*g->n-7)); @ @<Call |f(u,v)| for each Delaunay edge...@>= a=(arc *)min_arc; b=(arc *)max_arc; for (; a<next_arc; a++,b--) (*f)(a->vert,b->vert); @ The last and probably most crucial component of the data structure is the collection of {\sl branch nodes}, which will be linked together into a binary tree. Given a new vertex |w|, we will ascertain what triangle it belongs to by starting at the root of this tree and executing a sequence of instructions, each of which has the form `if |w| lies to the right of the straight line from |u| to~|v| then go to $\alpha$ else go to~$\beta$', where $\alpha$ and~$\beta$ are nodes that continue the search. This process continues until we reach a terminal node, which says `congratulations, you're done, |w|~is in triangle such-and-such'. The terminal node points to one of the three arcs bounding that triangle. If a vertex of the triangle is~$\infty$, the terminal node points to the arc whose |vert| pointer is~|NULL|. @<Type...@>= typedef struct n_struct { Vertex *u; /* first vertex, or |NULL| if this is a terminal node */ Vertex *v; /* second vertex, or pointer to the triangle corresponding to a terminal node */ struct n_struct *l; /* go here if |w| lies to the left of $uv$ */ struct n_struct *r; /* go here if |w| lies to the right of $uv$ */ } node; @ The search tree just described is actually a dag (a directed acyclic graph), because it has overlapping subtrees. As the algorithm proceeds, the dag gets bigger and bigger, since the number of triangles keeps growing. Instructions are never deleted; we just extend the dag by substituting new branches for nodes that once were terminal. The expected number of nodes in this dag is $O(n)$ when there are $n$~vertices, if we input the vertices in random order. But it can be as high as order~$n^2$ in the worst case. So our program will allocate blocks of nodes dynamically instead of assuming a maximum size. @d nodes_per_block 127 /* on most computers we want it $\equiv 15$ (mod 16) */ @d new_node(x) if (next_node==max_node) { x=gb_typed_alloc(nodes_per_block,node,working_storage); if (x==NULL) { gb_free(working_storage); /* release |delaunay|'s auxiliary memory */ return; /* |gb_trouble_code| is nonzero */ } next_node=x+1; max_node=x+nodes_per_block; }@+else x=next_node++; @# @d terminal_node(x,p) {@+new_node(x); /* allocate a new node */ x->v=(Vertex*)(p); /* make it point to a given arc from the triangle */ } /* note that |x->u==NULL|, representing a terminal node */ @<Local variables for |delaunay|@>= node *next_node; /* the first yet-unused node slot in the current block of nodes */ node *max_node; /* address of nonexistent node following the current block of nodes */ node root_node; /* start here to locate a vertex in its triangle */ Area working_storage; /* where |delaunay| builds its triangulation */ @ The algorithm begins with a trivial triangulation that contains only the first two vertices, together with two ``triangles'' extending to infinity at their left and right. @<Initialize the data structures@>= next_node=max_node=NULL; init_area(working_storage); @<Initialize the array of arcs@>; u=g->vertices; v=u+1; @<Make two ``triangles'' for |u|, |v|, and $\infty$@>; @ We'll need a bunch of local variables to do elementary operations on data structures. @<Local variables for |delaunay|@>= Vertex *p, *q, *r, *s, *t, *tp, *tpp, *u, *v; arc *a,*aa,*b,*c,*d, *e; node *x,*y,*yp,*ypp; @ @<Make two ``triangles'' for |u|, |v|, and $\infty$@>= root_node.u=u; root_node.v=v; a=next_arc; terminal_node(x,a+1); root_node.l=x; a->vert=v;@+a->next=a+1;@+a->inst=x; (a+1)->next=a+2;@+(a+1)->inst=x; /* |(a+1)->vert=NULL|, representing $\infty$ */ (a+2)->vert=u;@+(a+2)->next=a;@+(a+2)->inst=x; mate(a,b); terminal_node(x,b-2); root_node.r=x; b->vert=u;@+b->next=b-2;@+b->inst=x; (b-2)->next=b-1;@+(b-2)->inst=x; /* |(b-2)->vert=NULL|, representing $\infty$ */ (b-1)->vert=v;@+(b-1)->next=b;@+(b-1)->inst=x; next_arc+=3; @*Delaunay updating. The main loop of the algorithm updates the data structure incrementally by adding one new vertex at a time. The new vertex will always be connected by an edge (i.e., by two arcs) to each of the vertices of the triangle that previously enclosed it. It might also deserve to be connected to other nearby vertices. @<Find the Delaunay triangulation...@>= if (g->n<2) return; /* no edges unless there are at least 2 vertices */ @<Initialize the data structures@>; for (p=g->vertices+2;p<g->vertices+g->n;p++) { @<Find an arc |a| on the boundary of the triangle containing |p|@>; @<Divide the triangle left of |a| into three triangles surrounding |p|@>; @<Explore the triangles surrounding |p|, ``flipping'' their neighbors until all triangles that should touch |p| are found@>; } @ We have set up the branch nodes so that they solve the triangle location problem. @<Find an arc |a| on the boundary of the triangle containing |p|@>= x=&root_node; do@+{ if (ccw(x->u,x->v,p)) x = x->l; else x = x->r; }@+while (x->u); a = (arc*) x->v; /* terminal node points to the arc we want */ @ Subdividing a triangle is an easy exercise in data structure manipulation, except that we must do something special when one of the vertices is infinite. Let's look carefully at what needs to be done. Suppose the triangle containing |p| has the vertices |q|, |r|, and |s| in counterclockwise order. Let |x| be the terminal node that points to the triangle~$\Delta qrs$. We want to change |x| so that we will be able to locate a future point of $\Delta qrs$ within either $\Delta pqr$, $\Delta prs$, or $\Delta psq$. If |q|, |r|, and |s| are finite, we will change |x| and add five new nodes as follows: $$\vbox{\halign{\hfil#:\enspace&#\hfil\cr $x$&if left of $rp$, go to $x''$, else go to $x'$;\cr $x'$&if left of $sp$, go to $y$, else go to $y'$;\cr $x''$&if left of $qp$, go to $y'$, else go to $y''$;\cr $y$&you're in $\Delta prs$;\cr $y'$&you're in $\Delta psq$;\cr $y''$&you're in $\Delta pqr$.\cr}}$$ But if, say, $q=\infty$, such instructions make no sense, because there are lines in all directions that run from $\infty$ to any point. In such a case we use ``wedges'' instead of triangles, as explained below. At the beginning of the following code, we have |x==a->inst|. @<Divide the triangle left of |a| into three triangles surrounding |p|@>= b=a->next;@+c=b->next; q=a->vert;@+r=b->vert;@+s=c->vert; @<Create new terminal nodes |y|, |yp|, |ypp|, and new arcs pointing to them@>; if (q==NULL) @<Compile instructions to update convex hull@> else {@+register node *xp; x->u=r;@+x->v=p; new_node(xp); xp->u=q;@+xp->v=p;@+xp->l=yp;@+xp->r=ypp; /* instruction $x''$ above */ x->l=xp; new_node(xp); xp->u=s;@+xp->v=p;@+xp->l=y;@+xp->r=yp; /* instruction $x'$ above */ x->r=xp; } @ The only subtle point here is that |q=a->vert| might be |NULL|. A terminal node must point to the proper arc of an infinite triangle. @<Create new terminal nodes |y|, |yp|, |ypp|, and new arcs pointing to them@>= terminal_node(yp,a);@+terminal_node(ypp,next_arc);@+terminal_node(y,c); c->inst=y;@+a->inst=yp;@+b->inst=ypp; mate(next_arc,e); a->next=e;@+b->next=e-1;@+c->next=e-2; next_arc->vert=q;@+next_arc->next=b;@+next_arc->inst=ypp; (next_arc+1)->vert=r;@+(next_arc+1)->next=c;@+(next_arc+1)->inst=y; (next_arc+2)->vert=s;@+(next_arc+2)->next=a;@+(next_arc+2)->inst=yp; e->vert=(e-1)->vert=(e-2)->vert=p; e->next=next_arc+2;@+(e-1)->next=next_arc;@+(e-2)->next=next_arc+1; e->inst=yp;@+(e-1)->inst=ypp;@+(e-2)->inst=y; next_arc += 3; @ Outside of the current convex hull, we have ``wedges'' instead of triangles. Wedges are exterior angles whose points lie outside an edge $rs$ of the convex hull, but not outside the next edge on the other side of point |r|. When a new point lies in such a wedge, we have to see if it also lies outside the edges $st$, $tu$, etc., in the clockwise direction, in which case the convex hull loses points $s$, $t$, etc., and we must update the new wedges accordingly. This was the hardest part of the program to prove correct; a complete proof can be found in {\sl Axioms and Hulls}. @<Compile...@>= {@+register node *xp; x->u=r;@+x->v=p;@+x->l=ypp; new_node(xp); xp->u=s;@+xp->v=p;@+xp->l=y;@+xp->r=yp; x->r=xp; mate(a,aa);@+d=aa->next;@+t=d->vert; while (t!=r && (ccw(p,s,t))) {@+register node *xpp; terminal_node(xpp,d); xp->r=d->inst; xp=d->inst; xp->u=t;@+xp->v=p;@+xp->l=xpp;@+xp->r=yp; flip(a,aa,d,s,NULL,t,p,xpp,yp); a=aa->next;@+mate(a,aa);@+d=aa->next; s=t;@+t=d->vert; yp->v=(Vertex*)a; } terminal_node(xp,d->next); x=d->inst;@+x->u=s;@+x->v=p;@+x->l=xp;@+x->r=yp; d->inst=xp;@+d->next->inst=xp;@+d->next->next->inst=xp; r=s; /* this value of |r| shortens the exploration step that follows */ } @ The updating process finishes by walking around the triangles that surround |p|, making sure that none of them are adjacent to triangles containing |p| in their circumcircle. (Such triangles are no longer in the Delaunay triangulation, by definition.) @<Explore...@>= while(1) { mate(c,d);@+e=d->next; t=d->vert;@+tp=c->vert;@+tpp=e->vert; if (tpp && incircle(tpp,tp,t,p)) { /* triangle $tt''t'$ no longer Delaunay */ register node *xp, *xpp; terminal_node(xp,e); terminal_node(xpp,d); x=c->inst;@+x->u=tpp;@+x->v=p;@+x->l=xp;@+x->r=xpp; x=d->inst;@+x->u=tpp;@+x->v=p;@+x->l=xp;@+x->r=xpp; flip(c,d,e,t,tp,tpp,p,xp,xpp); c=e; } else if (tp==r) break; else { mate(c->next,aa); c=aa->next; } } @ Here |d| is the mate of |c|, |e=d->next|, |t=d->vert|, |tp=c->vert|, and |tpp=e->vert|. The triangles $\Delta tt'p$ and $\Delta t'tt''$ to the left and right of arc~|c| are being replaced in the current triangulation by $\Delta ptt''$ and $\Delta t''t'p$, corresponding to terminal nodes |xp| and |xpp|. (The values of |t| and |tp| are not actually used, so some optimization is possible.) @<Other...@>= static void flip(c,d,e,t,tp,tpp,p,xp,xpp) arc *c,*d,*e; Vertex *t,*tp,*tpp,*p; node *xp,*xpp; {@+register arc *ep=e->next, *cp=c->next, *cpp=cp->next; e->next=c;@+c->next=cpp;@+cpp->next=e; e->inst=c->inst=cpp->inst=xp; c->vert=p; d->next=ep;@+ep->next=cp;@+cp->next=d; d->inst=ep->inst=cp->inst=xpp; d->vert=tpp; } @*Use of mileage data. The |delaunay| routine is now complete, and the only missing piece of code is the promised routine that generates planar graphs based on data from the real world. The subroutine call {\advance\thinmuskip 0mu plus 2mu |plane_miles(n,north_weight,west_weight,pop_weight, extend,prob,seed)|} will construct a planar graph with min$(128,n)$ vertices, where the vertices are exactly the same as the cities produced by the subroutine call |miles(n,north_weight,west_weight, pop_weight,0,0,seed)|. (As explained in module {\sc GB\_\,MILES}, the weight parameters |north_weight|, |west_weight|, and |pop_weight| are used to rank the cities by location and/or population.) The edges of the new graph are obtained by first constructing the Delaunay triangulation of those cities, based on a simple projection onto the plane using their latitude and longitude, then discarding each Delaunay edge with probability |prob/65536|. The length of each surviving edge is the same as the mileage between cities that would appear in the complete graph produced by |miles|. If |extend!=0|, an additional vertex representing $\infty$ is also included. The Delaunay triangulation includes edges of length |INFTY| connecting this vertex with all cities on the convex hull; these edges, like the others, are subject to being discarded with probability |prob/65536|. (See the description of |plane| for further comments about using |prob| to control the sparseness of the graph.) The weight parameters must satisfy $$ \vert|north_weight|\vert\le100{,}000,\quad \vert|west_weight|\vert\le100{,}000,\quad \vert|pop_weight|\vert\le100.$$ Vertices of the graph will appear in order of decreasing weight. The |seed| parameter defines the pseudo-random numbers used wherever a ``random'' choice between equal-weight vertices needs to be made, or when deciding whether to discard a Delaunay edge. @<The |plane_miles| routine@>= Graph *plane_miles(n,north_weight,west_weight,pop_weight,extend,prob,seed) unsigned long n; /* number of vertices desired */ long north_weight; /* coefficient of latitude in the weight function */ long west_weight; /* coefficient of longitude in the weight function */ long pop_weight; /* coefficient of population in the weight function */ unsigned long extend; /* should a point at infinity be included? */ unsigned long prob; /* probability of rejecting a Delaunay edge */ long seed; /* random number seed */ {@+Graph *new_graph; /* the graph constructed by |plane_miles| */ @<Use |miles| to set up the vertices of a graph@>; @<Compute the Delaunay triangulation and run through the Delaunay edges; reject them with probability |prob/65536|, otherwise append them with the road length in miles@>; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } gb_free(new_graph->aux_data); /* recycle special memory used by |miles| */ if (extend) new_graph->n++; /* make the ``infinite'' vertex legitimate */ return new_graph; } @ By setting the |max_distance| parameter to~1, we cause |miles| to produce a graph having the desired vertices but no edges. The vertices of this graph will have appropriate coordinate fields |x_coord|, |y_coord|, and~|z_coord|. @<Use |miles|...@>= if (extend) extra_n++; /* allocate one more vertex than usual */ if (n==0 || n>MAX_N) n=MAX_N; /* compute true number of vertices */ new_graph=miles(n,north_weight,west_weight,pop_weight,1L,0L,seed); if (new_graph==NULL) return NULL; /* |panic_code| has been set by |miles| */ sprintf(new_graph->id,"plane_miles(%lu,%ld,%ld,%ld,%lu,%lu,%ld)", n,north_weight,west_weight,pop_weight,extend,prob,seed); if (extend) extra_n--; /* restore |extra_n| to its previous value */ @ @<Compute the Delaunay triangulation and run through the Delaunay edges; reject them with probability |prob/65536|, otherwise append them with the road length in miles@>= gprob=prob; if (extend) { inf_vertex=new_graph->vertices+new_graph->n; inf_vertex->name=gb_save_string("INF"); inf_vertex->x_coord=inf_vertex->y_coord=inf_vertex->z_coord= -1; }@+else inf_vertex=NULL; delaunay(new_graph,new_mile_edge); @ The mileages will all have been negated by |miles|, so we make them positive again. @<Other...@>= static void new_mile_edge(u,v) Vertex *u,*v; { if ((gb_next_rand()>>15)>=gprob) { if (u) { if (v) gb_new_edge(u,v,-miles_distance(u,v)); else if (inf_vertex) gb_new_edge(u,inf_vertex,INFTY); }@+else if (inf_vertex) gb_new_edge(inf_vertex,v,INFTY); } } @* Index. As usual, we close with an index that shows where the identifiers of \\{gb\_plane} are defined and used. ��������������gb_raman.w������������������������������������������������������������������������������������������0000444�0001750�0001750�00000075236�06236445442�011162� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_\,RAMAN} \let\==\equiv % congruence sign \prerequisite{GB\_\,GRAPH} @* Introduction. This GraphBase module contains the |raman| subroutine, which creates a family of ``Ramanujan graphs'' based on a theory developed by Alexander Lubotzky, Ralph Phillips, and Peter Sarnak @^Lubotzky, Alexander@> @^Phillips, Ralph Saul@> @^Sarnak, Peter@> [see {\sl Combinatorica \bf8} (1988), 261--277]. Ramanujan graphs are defined by the following properties: @^Ramanujan graphs@> They are connected, undirected graphs in which every vertex has degree~$k$, and every eigenvalue of the adjacency matrix is either $\pm k$ or has absolute value $\le2\sqrt{\mathstrut k-1}$. Such graphs are known to have good expansion properties, small diameter, and relatively small independent sets; they cannot be colored with fewer than $k/\bigl(2\sqrt{\mathstrut k-1}\,\bigr)$ colors unless they are bipartite. The particular examples of Ramanujan graphs constructed here are based on interesting properties of quaternions with integer coefficients. An example of the use of this procedure can be found in the demo program called {\sc GIRTH}. @(gb_raman.h@>= extern Graph *raman(); @ The subroutine call |raman(p,q,type,reduce)| constructs an undirected graph in which each vertex has degree~|p+1|. The number of vertices is~|q+1| if |type=1|, or~${1\over2}q(q+1)$ if |type=2|, or ${1\over2}(q-1)q(q+1)$ if |type=3|, or |(q-1)q(q+1)| if |type=4|. The graph will be bipartite if and only if it has type~4. Parameters |p| and |q| must be distinct prime numbers, and |q|~must be odd. Furthermore there are additional restrictions: If |p=2|, the other parameter |q| must satisfy $q\bmod8\in\{1,3\}$ and $q\bmod13\in\{1,3,4,9,10,12\}$; this rules out about one fourth of all primes. Moreover, if |type=3| the value of |p| must be a quadratic residue modulo~$q$; in other words, there must be an integer~$x$ such that $x^2\=p$ (mod~$q$). If |type=4|, the value of |p| must not be a quadratic residue. If you specify |type=0|, the procedure will choose the largest permissible type (either 3 or~4); the value of the type selected will appear as part of the string placed in the resulting graph's |id| field. For example, if |type=0|, |p=2|, and |q=43|, a type~4 graph will be generated, because 2 is not a quadratic residue modulo~43. This graph will have $44\times43\times42=79464$ vertices, each of degree~3. (Notice that graphs of types 3 and~4 can be quite large even when |q| is rather small.) The largest permissible value of |q| is 46337; this is the largest prime whose square is less than $2^{31}$. Of course you would use it only for a graph of type~1. If |reduce| is nonzero, loops and multiple edges will be suppressed. In this case the degrees of some vertices might turn out to be less than~|p+1|, in spite of what was said above. Although type 4 graphs are bipartite, the vertices are not separated into two blocks as in other bipartite graphs produced by GraphBase routines. All edges of the graphs have length 1. @ If the |raman| routine encounters a problem, it returns |NULL| (\.{NULL}), after putting a code number into the external variable |panic_code|. This code number identifies the type of failure. Otherwise |raman| 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_trouble_code=0;@+return NULL;@+} @d dead_panic(c) {@+gb_free(working_storage);@+panic(c);@+} @d late_panic(c) {@+gb_recycle(new_graph);@+dead_panic(c);@+} @ The \CEE/ file \.{gb\_raman.c} has the following general shape: @p #include "gb_graph.h" /* we will use the {\sc GB\_\,GRAPH} data structures */ @h@# @<Type declarations@>@; @<Private variables and routines@>@; @# Graph *raman(p,q,type,reduce) long p; /* one less than the desired degree; must be prime */ long q; /* size parameter; must be prime and properly related to |type| */ unsigned long type; /* selector between different possible constructions */ unsigned long reduce; /* if nonzero, multiple edges and self-loops won't occur */ {@+@<Local variables@>@;@# @<Prepare tables for doing arithmetic modulo~|q|@>; @<Choose or verify the |type|, and determine the number |n| of vertices@>; @<Set up a graph with |n| vertices, and assign vertex labels@>; @<Compute |p+1| generators that will define the graph's edges@>; @<Append the edges@>; if (gb_trouble_code) late_panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ gb_free(working_storage); return new_graph; } @ @<Local var...@>= Graph *new_graph; /* the graph constructed by |raman| */ Area working_storage; /* place for auxiliary tables */ @* Brute force number theory. Instead of using routines like Euclid's algorithm to compute inverses and square roots modulo~|q|, we have plenty of time to build complete tables, since |q| is smaller than the number of vertices we will be generating. We will make three tables: |q_sqr[k]| will contain $k^2$ modulo~|q|; |q_sqrt[k]| will contain one of the values of $\sqrt{\mathstrut k}$ if $k$ is a quadratic residue; and |q_inv[k]| will contain the multiplicative inverse of~|k|. @<Private...@>= static long *q_sqr; /* squares */ static long *q_sqrt; /* square roots (or $-1$ if not a quadratic residue) */ static long *q_inv; /* reciprocals */ @ @<Prepare tables for doing arithmetic modulo~|q|@>= if (q<3 || q>46337) panic(very_bad_specs); /* |q| is way too small or way too big */ if (p<2) panic(very_bad_specs+1); /* |p| is way too small */ init_area(working_storage); q_sqr=gb_typed_alloc(3*q,long,working_storage); if (q_sqr==0) panic(no_room+1); q_sqrt=q_sqr+q; q_inv=q_sqrt+q; /* note that |gb_alloc| has initialized everything to zero */ @<Compute the |q_sqr| and |q_sqrt| tables@>; @<Find a primitive root |a|, modulo |q|, and its inverse |aa|@>; @<Compute the |q_inv| table@>; @ @<Compute the |q_sqr| and |q_sqrt| tables@>= for (a=1; a<q; a++) q_sqrt[a]=-1; for (a=1,aa=1; a<q; aa=(aa+a+a+1)%q,a++) { q_sqr[a]=aa; q_sqrt[aa]=q-a; /* the smaller square root will survive */ q_inv[aa]=-1; /* we make |q_inv[aa]| nonzero when |aa| can't be a primitive root */ } @ @<Local v...@>= register long a, aa, k; /* primary indices in loops */ long b, bb, c, cc, d, dd; /* secondary indices */ long n; /* the number of vertices */ long n_factor; /* either ${1\over2}(q-1)$ (type~3) or $q-1$ (type 4) */ register Vertex *v; /* the current vertex of interest */ @ Here we implicitly test that |q| is prime, by finding a primitive root whose powers generate everything. If |q| is not prime, its smallest divisor will cause the inner loop in this step to terminate with |k>=q|, because no power of that divisor will be congruent to~1. @<Find a primitive root |a|, modulo |q|, and its inverse |aa|@>= for (a=2; ; a++) if (q_inv[a]==0) { for (b=a,k=1; b!=1&&k<q; aa=b,b=(a*b)%q,k++) q_inv[b]=-1; if (k>=q) dead_panic(bad_specs+1); /* |q| is not prime */ if (k==q-1) break; /* good, |a| is the primitive root we seek */ } @ As soon as we have discovered a primitive root, it is easy to generate all the inverses. (We could also generate the discrete logarithms if we had a need for them.) We set |q_inv[0]=q|; this will be our internal representation of $\infty$. @<Compute the |q_inv| table@>= for (b=a,bb=aa; b!=bb; b=(a*b)%q,bb=(aa*bb)%q) q_inv[b]=bb,q_inv[bb]=b; q_inv[1]=1; q_inv[b]=b; /* at this point |b| must equal |q-1| */ q_inv[0]=q; @ The conditions we stated for validity of |q| when |p=2| are equivalent to the existence of $\sqrt{-2}$ and $\sqrt{13}$ modulo~|q|, according to the law of quadratic reciprocity (see, for example, {\sl Fundamental Algorithms}, exercise 1.2.4--47). @<Choose or verify the |type|...@>= if (p==2) { if (q_sqrt[13%q]<0 || q_sqrt[q-2]<0) dead_panic(bad_specs+2); /* improper prime to go with |p=2| */ } if ((a=p%q)==0) dead_panic(bad_specs+3); /* |p| divisible by |q| */ if (type==0) type=(q_sqrt[a]>0? 3: 4); n_factor=(type==3? (q-1)/2: q-1); switch (type) { case 1: n=q+1;@+break; case 2: n=q*(q+1)/2;@+break; default: if ((q_sqrt[a]>0 && type!=3) || (q_sqrt[a]<0 && type!=4))@/ dead_panic(bad_specs+4); /* wrong type for |p| modulo |q| */ if (q>1289) dead_panic(bad_specs+5); /* way too big for types 3, 4 */ n=n_factor*q*(q+1); break; } if (p>=(long)(0x3fffffff/n)) dead_panic(bad_specs+6); /* $(p+1)n\ge2^{30}$ */ @* The vertices. Graphs of type 1 have vertices from the set $\{0,1,\ldots,q-1,\infty\}$, namely the integers modulo~|q| with an additional ``infinite'' element thrown in. The idea is to operate on these quantities by adding constants, and/or multiplying by constants, and/or taking reciprocals, modulo~|q|. Graphs of type 2 have vertices that are unordered pairs of distinct elements from that same $(q+1)$-element set. Graphs of types 3 and 4 have vertices that are $2\times2$ matrices having nonzero determinants modulo~|q|. The determinants of type~3 matrices are, in fact, nonzero quadratic residues. We consider two matrices to be equivalent if one is obtained from the other by multiplying all entries by a constant (modulo~|q|); therefore we will normalize all the matrices so that the second row is either $(0,1)$ or has the form $(1,x)$ for some~$x$. The total number of equivalence classes of type~4 matrices obtainable in this way is $(q+1)q(q-1)$, because we can choose the second row in $q+1$ ways, after which there are two cases: Either the second row is $(0,1)$, and we can select the upper right corner element arbitrarily and choose the upper left corner element nonzero; or the second row is $(1,x)$, and we can select the upper left corner element arbitrarily and then choose an upper right corner element to make the determinant nonzero. For type~3 the counting is similar, except that ``nonzero'' becomes ``nonzero quadratic residue,'' hence there are exactly half as many choices. It is easy to verify that the equivalence classes of matrices that correspond to vertices in these graphs of types 3 and~4 are closed under matrix multiplication. Therefore the vertices can be regarded as the elements of finite groups. The type~3 group for a given |q| is often called the linear fractional group $LF(2,{\bf F}_q)$, or the projective special linear group $PSL(2,{\bf F}_q)$, or the linear simple group $L_2(q)$; it can also be regarded as the group of $2\times2$ matrices with determinant~1 (mod~$q$), when the matrix $A$ is considered equivalent to $-A$. (This group is a simple group for all primes |q>2|.) The type~4 group is officially known as the projective general linear group of degree~2 over the field of |q|~elements, $PGL(2,{\bf F}_q)$. @<Set up a graph...@>= new_graph=gb_new_graph(n); if (new_graph==NULL) dead_panic(no_room); /* out of memory before we try to add edges */ sprintf(new_graph->id,"raman(%ld,%ld,%lu,%lu)",p,q,type,reduce); strcpy(new_graph->util_types,"ZZZIIZIZZZZZZZ"); v=new_graph->vertices; switch(type) { case 1: @<Assign labels from the set $\{0,1,\ldots,q-1,\infty\}$@>;@+break; case 2: @<Assign labels for pairs of distinct elements@>;@+break; default: @<Assign projective matrix labels@>;@+break; } @ Type 1 graphs are the easiest to label. We store a serial number in utility field |x.I|, using $q$ to represent~$\infty$. @<Assign labels from the set $\{0,1,\ldots,q-1,\infty\}$@>= new_graph->util_types[4]='Z'; for (a=0;a<q;a++) { sprintf(name_buf,"%ld",a); v->name=gb_save_string(name_buf); v->x.I=a; v++; } v->name=gb_save_string("INF"); v->x.I=q; v++; @ @<Private...@>= static char name_buf[]="(1111,1111;1,1111)"; /* place to form vertex names */ @ The type 2 labels run from $\{0,1\}$ to $\{q-1,\infty\}$; we put the coefficients into |x.I| and |y.I|, where they might prove useful in some applications. @<Assign labels for pairs...@>= for (a=0;a<q;a++) for (aa=a+1;aa<=q;aa++) { if (aa==q) sprintf(name_buf,"{%ld,INF}",a); else sprintf(name_buf,"{%ld,%ld}",a,aa); v->name=gb_save_string(name_buf); v->x.I=a;@+v->y.I=aa; v++; } @ For graphs of types 3 and 4, we set the |x.I| and |y.I| fields to the elements of the first row of the matrix, and we set the |z.I| field equal to the ratio of the elements of the second row (again with $q$ representing~$\infty$). The vertices in this case consist of |q(q+1)| blocks of vertices having a given second row and a given element in the upper left or upper right position. Within each block of vertices, the determinants are respectively congruent modulo~|q| to $1^2$, $2^2$, \dots,~$({q-1\over2})^2$ in the case of type~3 graphs, or to 1,~2, \dots,~$q-1$ in the case of type~4. @<Assign projective matrix labels@>= new_graph->util_types[5]='I'; for (c=0;c<=q;c++) for (b=0;b<q;b++) for (a=1;a<=n_factor;a++) { v->z.I=c; if (c==q) { /* second row of matrix is $(0,1)$ */ v->y.I=b; v->x.I=(type==3? q_sqr[a]: a); /* determinant is $a^2$ or $a$ */ sprintf(name_buf,"(%ld,%ld;0,1)",v->x.I,b); }@+else { /* second row of matrix is $(1,c)$ */ v->x.I=b; v->y.I=(b*c+q-(type==3? q_sqr[a]: a))%q; /* determinant is $a^2$ or $a$ */ sprintf(name_buf,"(%ld,%ld;1,%ld)",b,v->y.I,c); } v->name=gb_save_string(name_buf); v++; } @* Group generators. We will define a set of |p+1| permutations $\{\pi_0, \pi_1,\ldots,\pi_p\}$ of the vertices, such that the arcs of our graph will go from $v$ to $v\pi_k$ for |0<=k<=p|. Thus, each path in the graph will be defined by a product of permutations; the cycles of the graph will correspond to vertices that are left fixed by a product of permutations. The graph will be undirected, because the inverse of each $\pi_k$ will also be one of the permutations of the generating set. In fact, each permutation $\pi_k$ will be defined by a $2\times2$ matrix. For graphs of types 3 and~4, the permutations will therefore correspond to certain vertices, and the vertex $v\pi_k$ will simply be the product of matrix $v$ by matrix $\pi_k$. For graphs of type 1, the permutations will be defined by linear fractional transformations, which are mappings of the form $$v\;\longmapsto\; {av+b\over cv+d}\bmod q\,.$$ This transformation applies to all $v\in\{0,1,\ldots,q-1,\infty\}$, under the usual conventions that $x/0=\infty$ when $x\ne0$ and $(x\infty+x')/(y\infty+y')=x/y$. The composition of two such transformations is again a linear fractional transformation, corresponding to the product of the two associated matrices $\bigl({a\,b\atop c\,d}\bigr)$. Graphs of type 2 will be handled just like graphs of type 1, except that we will compute the images of two distinct points $v=\{v_1,v_2\}$ under the linear fractional transformation. The two images will be distinct, because the transformation is invertible. When |p=2|, a special set of three generating matrices $\pi_0$, $\pi_1$, $\pi_2$ can be shown to define Ramanujan graphs; these matrices are described below. Otherwise |p| is odd, and the generators are based on the theory of integral quaternions. Integral quaternions, for our purposes, are quadruples of the form $\alpha=a_0+a_1i+a_2j+a_3k$, where $a_0$, $a_1$, $a_2$, and~$a_3$ are integers; we multiply them by using the associative but noncommutative multiplication rules $i^2=j^2=k^2=ijk=-1$. If we write $\alpha=a+A$, where $a$ is the ``scalar'' $a_0$ and $A$ is the ``vector'' $a_1i+a_2j+a_3k$, the product of quaternions $\alpha=a+A$ and $\beta=b+B$ can be expressed as $$(a+A)(b+B)=ab-A\cdot B+aB+bA+A\times B\,,$$ where $A\cdot B$ and $A\times B$ are the usual dot product and cross product of vectors. The conjugate of $\alpha=a+A$ is $\overline\alpha=a-A$, and we have $\alpha\overline\alpha=a_0^2+a_1^2+a_2^2+a_3^2$. This important quantity is called $N(\alpha)$, the norm of $\alpha$. It is not difficult to verify that $N(\alpha\beta)=N(\alpha)N(\beta)$, because of the basic identity $\overline{\mathstrut\alpha\beta}=\overline{\mathstrut\beta} \,\overline{\mathstrut\alpha}$ and the fact that $\alpha x=x\alpha$ when $x$ is scalar. Integral quaternions have a beautiful theory; for example, there is a nice variant of Euclid's algorithm by which we can compute the greatest common left divisor of any two integral quaternions having odd norm. This algorithm makes it possible to prove that integral quaternions whose coefficients are relatively prime can be canonically factored into quaternions whose norm is prime. However, the details of that theory are beyond the scope of this documentation. It will suffice for our purposes to observe that we can use quaternions to define the finite groups $PSL(2,{\bf F}_q)$ and $PGL(2,{\bf F}_q)$ in a different way from the definitions given earlier: Suppose we consider two quaternions to be equivalent if one is a nonzero scalar multiple of the other, modulo~$q$. Thus, for example, if $q=3$ we consider $1+4i-j$ to be equivalent to $1+i+2j$, and also equivalent to $2+2i+j$. It turns out that there are exactly $(q+1)q(q-1)$ such equivalence classes, when we omit quaternions whose norm is a multiple of~$q$; and they form a group under quaternion multiplication that is the same as the projective group of $2\times2$ matrices under matrix multiplication, modulo~|q|. One way to prove this is by means of the one-to-one correspondence $$a_0+a_1i+a_2j+a_3k\;\longleftrightarrow\; \left(\matrix{a_0+a_1g+a_3h&a_2+a_3g-a_1h\cr -a_2+a_3g-a_1h&a_0-a_1g-a_3h\cr}\right)\,,$$ where $g$ and $h$ are integers with $g^2+h^2\=-1$ (mod~|q|). Jacobi proved that the number of ways to represent @^Jacobi, Carl Gustav Jacob@> any odd number |p| as a sum of four squares $a_0^2+a_1^2+a_2^2+a_3^2$ is 8 times the sum of divisors of~|p|. [This fact appears in the concluding sentence of his monumental work {\sl Fundamenta Nova Theori\ae\ Functionum Ellipticorum}, K\"onigsberg, 1829.] In particular, when |p| is prime, the number of such representations is $8(p+1)$; in other words, there are exactly $8(p+1)$ quaternions $\alpha=a_0+a_1i+a_2j+a_3k$ with $N(\alpha)=p$. These quaternions form |p+1| equivalence classes under multiplication by the eight ``unit quaternions'' $\{\pm1,\pm i,\pm j,\pm k\}$. We will select one element from each equivalence class, and the resulting |p+1| quaternions will correspond to |p+1| matrices, which will generate the |p+1| arcs leading from each vertex in the graphs to be constructed. @<Type de...@>= typedef struct { long a0,a1,a2,a3; /* coefficients of a quaternion */ unsigned long bar; /* the index of the inverse (conjugate) quaternion */ } quaternion; @ A global variable |gen_count| will be declared below, indicating the number of generators found so far. When |p| isn't prime, we will find more than |p+1| solutions, so we allocate an extra slot in the |gen| table to hold a possible overflow entry. @<Compute |p+1| generators...@>= gen=gb_typed_alloc(p+2,quaternion,working_storage); if (gen==NULL) late_panic(no_room+2); /* not enough memory */ gen_count=0;@+max_gen_count=p+1; if (p==2) @<Fill the |gen| table with special generators@>@; else @<Fill the |gen| table with representatives of all quaternions having norm~|p|@>; if (gen_count!=max_gen_count) late_panic(bad_specs+7); /* |p| is not prime */ @ @<Private...@>= static quaternion *gen; /* table of the |p+1| generators */ @ As mentioned above, quaternions of norm |p| come in sets of 8, differing from each other only by unit multiples; we need to choose one of the~8. Suppose $a_0^2+a_1^2+a_2^2+a_3^2=p$. If $p\bmod4=1$, exactly one of the $a$'s will be odd; so we call it $a_0$ and assign it a positive sign. When $p\bmod4=3$, exactly one of the $a$'s will be even; we call it $a_0$, and if it is nonzero we make it positive. If $a_0=0$, we make sure that one of the others---say the rightmost appearance of the largest one---is positive. In this way we obtain a unique representative from each set of 8 equivalent quaternions. For example, the four quaternions of norm 3 are $\null\pm i\pm j+k$; the six of norm~5 are $1\pm2i$, $1\pm2j$, $1\pm2k$. In the program here we generate solutions to $a^2+b^2+c^2+d^2=p$ when $a\not\=b\=c\=d$ (mod~2) and $b\le c\le d$. The variables |aa|, |bb|, and |cc| hold the respective values $p-a^2-b^2-c^2-d^2$, $p-a^2-3b^2$, and $p-a^2-2c^2$. The |for| statements use the fact that $a^2$ increases by $4(a+1)$ when $a$ increases by~2. @<Fill the |gen| table with representatives...@>= {@+long sa,sb; /* $p-a^2$, $p-a^2-b^2$ */ long pp=(p>>1)&1; /* 0 if $p\bmod4=1$, \ 1 if $p\bmod4=3$ */ for (a=1-pp,sa=p-a;sa>0;sa-=(a+1)<<2,a+=2) for (b=pp,sb=sa-b,bb=sb-b-b;bb>=0;bb-=12*(b+1),sb-=(b+1)<<2,b+=2) for (c=b,cc=bb;cc>=0;cc-=(c+1)<<3,c+=2) for (d=c,aa=cc;aa>=0;aa-=(d+1)<<2,d+=2) if (aa==0) @<Deposit the quaternions associated with $a+bi+cj+dk$@>; @<Change the |gen| table to matrix format@>; } @ If |a>0| and |0<b<c<d|, we obtain 48 different classes of quaternions having the same norm by permuting $\{b,c,d\}$ in six ways and attaching signs to each permutation in eight ways. This happens, for example, when $p=71$ and $(a,b,c,d)=(6,1,3,5)$. Fewer quaternions arise when |a=0| or |0=b| or |b=c| or |c=d|. The inverse of the matrix corresponding to a quaternion is the matrix corresponding to the conjugate quaternion. Therefore a generating matrix $\pi_k$ will be its own inverse if and only if it comes from a quaternion with |a=0|. It is convenient to have a subroutine that deposits a new quaternion and its conjugate into the table of generators. @<Private...@>= static unsigned long gen_count; /* the next available quaternion slot */ static unsigned long max_gen_count; /* $p+1$, stored as a global variable */ static void deposit(a,b,c,d) long a,b,c,d; /* a solution to $a^2+b^2+c^2+d^2=p$ */ { if (gen_count>=max_gen_count) /* oops, we already found |p+1| solutions */ gen_count=max_gen_count+1; /* this will happen only if |p| isn't prime */ else { gen[gen_count].a0=gen[gen_count+1].a0=a; gen[gen_count].a1=b;@+gen[gen_count+1].a1=-b; gen[gen_count].a2=c;@+gen[gen_count+1].a2=-c; gen[gen_count].a3=d;@+gen[gen_count+1].a3=-d; if (a) { gen[gen_count].bar=gen_count+1; gen[gen_count+1].bar=gen_count; gen_count+=2; }@+else { gen[gen_count].bar=gen_count; gen_count++; } } } @ @<Deposit...@>= { deposit(a,b,c,d); if (b) { deposit(a,-b,c,d);@+deposit(a,-b,-c,d); } if (c) deposit(a,b,-c,d); if (b<c) { deposit(a,c,b,d);@+deposit(a,-c,b,d);@+deposit(a,c,d,b);@+ deposit(a,-c,d,b); if (b) { deposit(a,c,-b,d);@+deposit(a,-c,-b,d);@+deposit(a,c,d,-b);@+ deposit(a,-c,d,-b); } } if (c<d) { deposit(a,b,d,c);@+deposit(a,d,b,c); if (b) { deposit(a,-b,d,c);@+deposit(a,-b,d,-c);@+deposit(a,d,-b,c);@+ deposit(a,d,-b,-c); } if (c) { deposit(a,b,d,-c);@+deposit(a,d,b,-c); } if (b<c) { deposit(a,d,c,b);@+deposit(a,d,-c,b); if (b) { deposit(a,d,c,-b);@+deposit(a,d,-c,-b); } } } } @ Once we've found the generators in quaternion form, we want to convert them to $2\times2$ matrices, using the correspondence mentioned earlier: $$a_0+a_1i+a_2j+a_3k\;\longleftrightarrow\; \left(\matrix{a_0+a_1g+a_3h&a_2+a_3g-a_1h\cr -a_2+a_3g-a_1h&a_0-a_1g-a_3h\cr}\right)\,,$$ where $g$ and $h$ are integers with $g^2+h^2\=-1$ (mod~|q|). Appropriate values for $g$ and~$h$ can always be found by the formulas $$g=\sqrt{\mathstrut k}\qquad\hbox{and}\qquad h=\sqrt{\mathstrut q-1-k},$$ where $k$ is the largest quadratic residue modulo~|q|. For if $q-1$ is not a quadratic residue, and if $k+1$ isn't a residue either, then $q-1-k$ must be a quadratic residue because it is congruent to the product $(q-1)(k+1)$ of nonresidues. (We will have |h=0| if and only if $q\bmod4=1$; |h=1| if and only if $q\bmod8=3$; $h=\sqrt{\mathstrut2}$ if and only if $q\bmod24=7$ or 15; etc.) @<Change the |gen| table to matrix format@>= {@+register long g,h; long a00,a01,a10,a11; /* entries of $2\times2$ matrix */ for (k=q-1;q_sqrt[k]<0;k--) ; /* find the largest quadratic residue, |k| */ g=q_sqrt[k];@+h=q_sqrt[q-1-k]; for (k=p;k>=0;k--) { a00=(gen[k].a0+g*gen[k].a1+h*gen[k].a3)%q; if (a00<0) a00+=q; a11=(gen[k].a0-g*gen[k].a1-h*gen[k].a3)%q; if (a11<0) a11+=q; a01=(gen[k].a2+g*gen[k].a3-h*gen[k].a1)%q; if (a01<0) a01+=q; a10=(-gen[k].a2+g*gen[k].a3-h*gen[k].a1)%q; if (a10<0) a10+=q; gen[k].a0=a00;@+gen[k].a1=a01;@+gen[k].a2=a10;@+gen[k].a3=a11; } } @ When |p=2|, the following three appropriate generating matrices have been found by Patrick~Chiu: @^Chiu, Patrick@> $$\left(\matrix{1&0\cr 0&-1\cr}\right)\,,\qquad \left(\matrix{2+s&t\cr t&2-s\cr}\right)\,,\qquad\hbox{and}\qquad \left(\matrix{2-s&-t\cr-t&2+s\cr}\right)\,,$$ where $s^2\=-2$ and $t^2\=-26$ (mod~$q$). The determinants of these matrices are respectively $-1$, $32$, and~$32$; the product of the second and third matrices is 32 times the identity matrix. Notice that when 2 is a quadratic residue (this happens when $q=8k+1$), the determinants are all quadratic residues, so we get a graph of type~3. When 2 is a quadratic nonresidue (which happens when $q=8k+3$), the determinants are all nonresidues, so we get a graph of type~4. @<Fill the |gen| table with special generators@>= {@+long s=q_sqrt[q-2], t=(q_sqrt[13%q]*s)%q; gen[0].a0=1;@+gen[0].a1=gen[0].a2=0;@+gen[0].a3=q-1;@+gen[0].bar=0; gen[1].a0=gen[2].a3=(2+s)%q; gen[1].a1=gen[1].a2=t; gen[2].a1=gen[2].a2=q-t; gen[1].a3=gen[2].a0=(q+2-s)%q; gen[1].bar=2;@+gen[2].bar=1; gen_count=3; } @* Constructing the edges. The remaining task is to use the permutations defined by the |gen| table to create the arcs of the graph and their inverses. The |ref| fields in each arc will refer to the permutation leading to the arc. In most cases each vertex |v| will have degree exactly |p+1|, and the edges emanating from it will appear in a linked list having the respective |ref| fields 0,~1, \dots,~|p| in order. However, if |reduce| is nonzero, self-loops and multiple edges will be eliminated, so the degree might be less than |p+1|; in this case the |ref| fields will still be in ascending order, but some generators won't be referenced. There is a subtle case where |reduce=0| but the degree of a vertex might actually be greater than |p+1|. We want the graph |g| generated by |raman| to satisfy the conventions for undirected graphs stated in {\sc GB\_\,GRAPH}; therefore, if any of the generating permutations has a fixed point, we will create two arcs for that fixed point, and the corresponding vertex |v| will have an edge running to itself. Since each edge consists of two arcs, such an edge will produce two consecutive entries in the list |v->arcs|. If the generating permutation happens to be its own inverse, there will be two consecutive entries with the same |ref| field; this means there will be more than |p+1| entries in |v->arcs|, and the total number of arcs |g->m| will exceed |(p+1)n|. Self-inverse generating permutations arise only when |p=2| or when $p$ is expressible as a sum of three odd squares (hence $p\bmod8=3$); and such permutations will have fixed points only when |type<3|. Therefore this anomaly does not arise often. But it does occur, for example, in the smallest graph generated by |raman|, namely when |p=2|, |q=3|, and |type=1|, when there are 4~vertices and 14 (not~12) arcs. @d ref a.I /* the |ref| field of an arc refers to its permutation number */ @<Append the edges@>= for (k=p;k>=0;k--) {@+long kk; if ((kk=gen[k].bar)<=k) /* we assume that |kk=k| or |kk=k-1| */ for (v=new_graph->vertices;v<new_graph->vertices+n;v++) { register Vertex* u; @<Compute the image, |u|, of |v| under the permutation defined by |gen[k]|@>; if (u==v) { if (!reduce) { gb_new_edge(v,v,1L); v->arcs->ref=kk;@+(v->arcs+1)->ref=k; /* see the remarks above regarding the case |kk=k| */ } }@+else {@+register Arc* ap; if (u->arcs && u->arcs->ref==kk) continue; /* |kk=k| and we've already done this two-cycle */ else if (reduce) for (ap=v->arcs;ap;ap=ap->next) if (ap->tip==u) goto done; /* there's already an edge between |u| and |v| */ gb_new_edge(v,u,1L); v->arcs->ref=k;@+u->arcs->ref=kk; if ((ap=v->arcs->next)!=NULL && ap->ref==kk) { v->arcs->next=ap->next;@+ap->next=v->arcs;@+v->arcs=ap; } /* now the |v->arcs| list has |ref| fields in order again */ done:; } } } @ For graphs of types 3 and 4, our job is to compute a $2\times2$ matrix product, reduce it modulo~|q|, and find the appropriate equivalence class~|u|. @<Compute the image, |u|, of |v| under the permutation defined by |gen[k]|@>= if (type<3) @<Compute the image, |u|, of |v| under the linear fractional transformation defined by |gen[k]|@>@; else {@+long a00=gen[k].a0,a01=gen[k].a1,a10=gen[k].a2,a11=gen[k].a3; a=v->x.I;@+b=v->y.I; if (v->z.I==q) c=0,d=1; else c=1,d=v->z.I; @<Compute the matrix product |(aa,bb;cc,dd)=(a,b;c,d)*(a00,a01;a10,a11)|@>; a=(cc? q_inv[cc]: q_inv[dd]); /* now |a| is a normalization factor */ d=(a*dd)%q;@+c=(a*cc)%q;@+b=(a*bb)%q;@+a=(a*aa)%q; @<Set |u| to the vertex whose label is |(a,b;c,d)|@>; } @ @<Compute the matrix product...@>= aa=(a*a00+b*a10)%q; bb=(a*a01+b*a11)%q; cc=(c*a00+d*a10)%q; dd=(c*a01+d*a11)%q; @ @<Set |u|...@>= if (c==0) d=q,aa=a; else { aa=(a*d-b)%q; if (aa<0) aa+=q; b=a; } /* now |aa| is the determinant of the matrix */ u=new_graph->vertices+((d*q+b)*n_factor+(type==3? q_sqrt[aa]: aa)-1); @* Linear fractional transformations. Given a nonsingular $2\times2$ matrix $\bigl({a\,b\atop c\,d}\bigr)$, the linear fractional transformation $z\mapsto(az+b)/(cz+d)$ is defined modulo~$q$ by the following subroutine. We assume that the matrix $\bigl({a\,b\atop c\,d}\bigr)$ appears in row |k| of the |gen| table. @<Private...@>= static long lin_frac(a,k) long a; /* the number being transformed; $q$ represents $\infty$ */ long k; /* index into |gen| table */ {@+register long q=q_inv[0]; /* the modulus */ long a00=gen[k].a0, a01=gen[k].a1, a10=gen[k].a2, a11=gen[k].a3; /* the coefficients */ register long num, den; /* numerator and denominator */ if (a==q) num=a00, den=a10; else num=(a00*a+a01)%q, den=(a10*a+a11)%q; if (den==0) return q; else return (num*q_inv[den])%q; } @ We are computing the same values of |lin_frac| over and over again in type~2 graphs, but the author was too lazy to optimize this. @<Compute the image, |u|, of |v| under the linear fractional transformation defined by |gen[k]|@>= if (type==1) u=new_graph->vertices+lin_frac(v->x.I,k); else { a=lin_frac(v->x.I,k);@+aa=lin_frac(v->y.I,k); u=new_graph->vertices+(a<aa? (a*(2*q-1-a))/2+aa-1: (aa*(2*q-1-aa))/2+a-1); } @* Index. Here is a list that shows where the identifiers of this program are defined and used. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gb_rand.w�������������������������������������������������������������������������������������������0000444�0001750�0001750�00000056047�10070062222�010765� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_\,RAND} \prerequisite{GB\_\,GRAPH} @*Random graphs. This GraphBase module provides two external subroutines called |random_graph| and |random_bigraph|, which generate graphs in which the arcs or edges have been selected ``at random.'' A third subroutine, |random_lengths|, randomizes the lengths of the arcs of a given graph. The performance of algorithms on such graphs can fruitfully be compared to their performance on the nonrandom graphs generated by other GraphBase routines. Before reading this code, the reader should be familiar with the basic data structures and conventions described in {\sc GB\_\,GRAPH}. The routines in {\sc GB\_\,GRAPH} are loaded together with all GraphBase applications, and the programs below are typical illustrations of how to use them. @d random_graph r_graph /* abbreviations for Procrustean external linkage */ @d random_bigraph r_bigraph @d random_lengths r_lengths @(gb_rand.h@>= #define random_graph r_graph /* users of {\sc GB\_\,RAND} should include this header info */ #define random_bigraph r_bigraph #define random_lengths r_lengths extern Graph *random_graph(); extern Graph *random_bigraph(); extern long random_lengths(); @ Here is an overview of the file \.{gb\_rand.c}, the \CEE/ code for the routines in question. @p #include "gb_graph.h" /* this header file teaches \CEE/ about GraphBase */ #include "gb_flip.h" /* we will use the {\sc GB\_\,FLIP} routines for random numbers */ @h@# @<Private declarations@>@; @<Internal functions@>@; @<External functions@> @ The procedure |random_graph(n,m,multi,self,directed,dist_from,dist_to, min_len,max_len,seed)| is designed to produce a pseudo-random graph with |n| vertices and |m| arcs or edges, using pseudo-random numbers that depend on |seed| in a system-independent fashion. The remaining parameters specify a variety of options: $$\vcenter{\halign{#\hfil\cr |multi!=0| permits duplicate arcs;\cr |self!=0| permits self-loops (arcs from a vertex to itself);\cr |directed!=0| makes the graph directed; otherwise each arc becomes an undirected edge;\cr |dist_from| and |dist_to| specify probability distributions on the arcs;\cr |min_len| and |max_len| bound the arc lengths, which will be uniformly distributed between these limits.\cr }}$$ If |dist_from| or |dist_to| are |NULL|, the probability distribution is uniform over vertices; otherwise the \\{dist} parameter points to an array of |n| nonnegative integers that sum to $2^{30}$, specifying the respective probabilities (times $2^{30}$) that each given vertex will appear as the source or destination of the random arcs. A special option |multi=-1| is provided. This acts exactly like |multi=1|, except that arcs are not physically duplicated in computer memory---they are replaced by a single arc whose length is the minimum of all arcs having a common source and destination. The vertices are named simply |"0"|, |"1"|, |"2"|, and so on. @ Examples: |random_graph(1000,5000,0,0,0,NULL,NULL,1,1,0)| creates a random undirected graph with 1000 vertices and 5000 edges (hence 10000 arcs) of length~1, having no duplicate edges or self-loops. There are ${1000\choose2}=499500$ possible undirected edges on 1000 vertices; hence there are exactly $499500\choose5000$ possible graphs meeting these specifications. Every such graph would be equally likely, if |random_graph| had access to an ideal source of random numbers. The GraphBase programs are designed to be system-independent, so that identical graphs will be obtained by everybody who asks for |random_graph(1000,5000,0,0,0,NULL,NULL,1,1,0)|. Equivalent experiments on algorithms for graph manipulation can therefore be performed by researchers in different parts of the world. The subroutine call |random_graph(1000,5000,0,0,0,NULL,NULL,1,1,s)| will produce different graphs when the random seed |s| varies; however, the graph for any particular value of~|s| will be the same on all computers. The seed value can be any integer in the range $0\le s<2^{31}$. To get a random directed graph, allowing self-loops and repeated arcs, and with a uniform distribution on vertices, ask for $$\hbox{|random_graph(n,m,1,1,1,NULL,NULL,1,1,s)|}.$$ Each of the $m$ arcs of that digraph has probability $1/n^2$ of being from $u$ to $v$, for all $u$ and~$v$. If self-loops are disallowed (by changing `|1,1,1|' to `|1,0,1|'), each arc has probability $1/(n^2-n)$ of being from $u$ to $v$, for all $u\ne v$. To get a random directed graph in which vertex $k$ is twice as likely as vertex $k+1$ to be the source of an arc but only half as likely to be the destination of an arc, for all~$k$, try $$\hbox{|random_graph(31,m,1,1,1,d0,d1,0,255,s)|}$$ where the arrays |d0| and |d1| have the static declarations $$\vbox{ \hbox{|long d0[31]={0x20000000,0x10000000,@[@t\dots@>@],4,2,1,1};|} \hbox{|long d1[31]={1,1,2,4,@[@t\dots@>@],0x10000000,0x20000000};|}}$$ then about 1/4 of the arcs will run from 0 to 30, while arcs from 30 to 0 will be extremely rare (occurring with probability $2^{-60}$). Incidentally, the arc lengths in this example will be random bytes, uniformly distributed between 0 and 255, because |min_len=0| and |max_len=255|. If we forbid repeated arcs in this example, by setting |multi=0|, the effect is to discard all arcs having the same source and destination as a previous arc, regardless of length. In such a case |m|~had better not be too large, because the algorithm will keep going until it has found |m| distinct arcs, and many arcs are quite rare indeed; they will probably not be found until hundreds of centuries have elapsed. A random bipartite graph can also be obtained as a special case of |random_graph|; this case is explained below. Semantics: If |multi=directed=0| and |self!=0|, we have an undirected graph without duplicate edges but with self-loops permitted. A self-loop then consists of two identical self-arcs, in spite of the fact that |multi=0|. @ If the |random_graph| routine encounters a problem, it returns |NULL|, after putting a code number into the external variable |panic_code|. This code number identifies the type of failure. Otherwise |random_graph| returns a pointer to the newly created graph and leaves |panic_code| unchanged. The |gb_trouble_code| will be cleared to zero after |random_graph| has acted. @d panic(c) @+{@+panic_code=c;@+gb_trouble_code=0;@+return NULL;@+} @<External f...@>= Graph *random_graph(n,m,multi,self,directed,dist_from,dist_to,min_len,max_len, seed) unsigned long n; /* number of vertices desired */ unsigned long m; /* number of arcs or edges desired */ long multi; /* allow duplicate arcs? */ long self; /* allow self loops? */ long directed; /* directed graph? */ long *dist_from; /* distribution of arc sources */ long *dist_to; /* distribution of arc destinations */ long min_len,max_len; /* bounds on random lengths */ long seed; /* random number seed */ {@+@<Local variables@>@; @# if (n==0) panic(bad_specs); /* we gotta have a vertex */ if (min_len>max_len) panic(very_bad_specs); /* what are you trying to do? */ if (((unsigned long)(max_len))-((unsigned long)(min_len))>= ((unsigned long)0x80000000)) panic(bad_specs+1); /* too much range */ @<Check the distribution parameters@>; gb_init_rand(seed); @<Create a graph with |n| vertices and no arcs@>; @<Build tables for nonuniform distributions, if needed@>; for (mm=m; mm; mm--) @<Add a random arc or a random edge@>; trouble: if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } gb_free(new_graph->aux_data); return new_graph; } @ @<Local var...@>= Graph *new_graph; /* the graph constructed by |random_graph| */ long mm; /* the number of arcs or edges we still need to generate */ register long k; /* vertex being processed */ @ @d dist_code(x) (x? "dist": "0") @<Create a graph with |n| vertices and no arcs@>= new_graph=gb_new_graph(n); if (new_graph==NULL) panic(no_room); /* out of memory before we're even started */ for (k=0; k<n; k++) { sprintf(name_buffer,"%ld",k); (new_graph->vertices+k)->name=gb_save_string(name_buffer); } sprintf(new_graph->id,"random_graph(%lu,%lu,%d,%d,%d,%s,%s,%ld,%ld,%ld)",@| n,m,multi>0?1:multi<0?-1:0,self?1:0,directed?1:0,@| dist_code(dist_from),dist_code(dist_to),min_len,max_len,seed); @ @<Private d...@>= static char name_buffer[]="9999999999"; @ @d rand_len (min_len==max_len?min_len:min_len+gb_unif_rand(max_len-min_len+1)) @<Add a random arc or a random edge@>= {@+register Vertex *u,*v; repeat: if (dist_from) @<Generate a random vertex |u| according to |dist_from|@>@; else u=new_graph->vertices+gb_unif_rand(n); if (dist_to) @<Generate a random vertex |v| according to |dist_to|@>@; else v=new_graph->vertices+gb_unif_rand(n); if (u==v && !self) goto repeat; if (multi<=0) @<Search for duplicate arcs or edges; |goto repeat| or |done| if found@>; if (directed) gb_new_arc(u,v,rand_len); else gb_new_edge(u,v,rand_len); done:; } @ When we decrease the length of an existing edge, we use the fact that its two arcs are adjacent in memory. If |u==v| in this case, we encounter the first of two mated arcs before seeing the second; hence the mate of the arc we find is in location |a+1| when |u<=v|, and in location |a-1| when |u>v|. We must exit to location |trouble| if memory has been exhausted; otherwise there is a danger of an infinite loop, with |dummy_arc->next =dummy_arc|. @<Search for duplicate arcs or edges; |goto repeat| or |done| if found@>= if (gb_trouble_code) goto trouble; else {@+register Arc *a; long len; /* length of new arc or edge being combined with previous */ for (a=u->arcs; a; a=a->next) if (a->tip==v) if (multi==0) goto repeat; /* reject a duplicate arc */ else { /* |multi<0| */ len=rand_len; if (len<a->len) { a->len=len; if (!directed) { if (u<=v) (a+1)->len=len; else (a-1)->len=len; } } goto done; } } @* Nonuniform random number generation. The |random_graph| procedure is complete except for the parts that handle general distributions |dist_from| and |dist_to|. Before attempting to generate those distributions, we had better check them to make sure that the specifications are well formed; otherwise disaster might ensue later. This part of the program is easy. @<Check the distribution parameters@>= {@+register long acc; /* sum of probabilities */ register long *p; /* pointer to current probability of interest */ if (dist_from) { for (acc=0,@,p=dist_from; p<dist_from+n; p++) { if (*p<0) panic(invalid_operand); /* |dist_from| contains a negative entry */ if (*p>0x40000000-acc) panic(invalid_operand+1); /* probability too high */ acc+=*p; } if (acc!=0x40000000) panic(invalid_operand+2); /* |dist_from| table doesn't sum to $2^{30}$ */ } if (dist_to) { for (acc=0,@,p=dist_to; p<dist_to+n; p++) { if (*p<0) panic(invalid_operand+5); /* |dist_to| contains a negative entry */ if (*p>0x40000000-acc) panic(invalid_operand+6); /* probability too high */ acc+=*p; } if (acc!=0x40000000) panic(invalid_operand+7); /* |dist_to| table doesn't sum to $2^{30}$ */ } } @ We generate nonuniform distributions by using Walker's alias @^Walker, Alistair J.@> method (see, for example, {\sl Seminumerical Algorithms}, second edition, exercise 3.4.1--7). Walker's method involves setting up ``magic'' tables of length |nn|, where |nn| is the smallest power of~2 that is |>=n|. @f magic_entry int @<Local v...@>= long nn=1; /* this will be increased to $2^{\lceil\mskip1mu\lg n\rceil}$ */ long kk=31; /* this will be decreased to $31-\lceil\mskip1mu\lg n\rceil$ */ magic_entry *from_table, *to_table; /* alias tables */ @ @<Build...@>= { if (dist_from) { while (nn<n) nn+=nn, kk--; from_table=walker(n,nn,dist_from,new_graph); } if (dist_to) { while (nn<n) nn+=nn, kk--; to_table=walker(n,nn,dist_to,new_graph); } if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } } @ @<Private...@>= typedef struct { long prob; /* a probability, multiplied by $2^{31}$ and translated */ long inx; /* index that might be selected */ } @[magic_entry@]; @ Once the magic tables have been set up, we can generate nonuniform vertices by using the following code: @<Generate a random vertex |u|...@>= {@+register magic_entry *magic; register long uu=gb_next_rand(); /* uniform random number */ k=uu>>kk; magic=from_table+k; if (uu<=magic->prob) u=new_graph->vertices+k; else u=new_graph->vertices+magic->inx; } @ @<Generate a random vertex |v|...@>= {@+register magic_entry *magic; register long uu=gb_next_rand(); /* uniform random number */ k=uu>>kk; magic=to_table+k; if (uu<=magic->prob) v=new_graph->vertices+k; else v=new_graph->vertices+magic->inx; } @ So all we have to do is set up those magic tables. If |uu| is a uniform random integer between 0 and $2^{31}-1$, the index |k=uu>>kk| is a uniform random integer between 0 and |nn-1|, because of the relation between |nn| and |kk|. Once |k| is computed, the code above selects vertex~|k| with probability |(p+1-(k<<kk))|/$2^{31}$, where |p=magic->prob| and |magic| is the $k$th element of the magic table; otherwise the code selects vertex |magic->inx|. The trick is to set things up so that each vertex is selected with the proper overall probability. Let's imagine that the given distribution vector has length |nn|, instead of~|n|, by extending it if necessary with zeroes. Then the average entry among these |nn| integers is exactly $t=2^{30}/|nn|$. If some entry, say entry~|i|, exceeds |t|, there must be another entry that's less than |t|, say entry~|j|. We can set the $j$th entry of the magic table so that its |prob| field selects vertex~$j$ with the correct probability, and so that its |inx| field equals~|i|. Then we are selecting vertex~|i| with a certain residual probability; so we subtract that residual from |i|'s present probability, and repeat the process with vertex~|j| eliminated. The average of the remaining entries is still~|t|, so we can repeat this procedure until all remaining entries are exactly equal to~|t|. The rest is easy. During the calculation, we maintain two linked lists of |(prob,inx)| pairs. The |hi| list contains entries with |prob>t|, and the |lo| list contains the rest. During this part of the computation we call these list elements `nodes', and we use the field names |key| and~|j| instead of |prob| and |inx|. @<Private...@>= typedef struct node_struct { long key; /* a numeric quantity */ struct node_struct *link; /* the next node on the list */ long j; /* a vertex number to be selected with probability $|key|/2^{30}$ */ } node; static Area temp_nodes; /* nodes will be allocated in this area */ static node *base_node; /* beginning of a block of nodes */ @ @<Internal...@>= static magic_entry *walker(n,nn,dist,g) long n; /* length of |dist| vector */ long nn; /* $2^{\lceil\mskip1mu\lg n\rceil}$ */ register long *dist; /* start of distribution table, which sums to $2^{30}$ */ Graph *g; /* tables will be allocated for this graph's vertices */ {@+magic_entry *table; /* this will be the magic table we compute */ long t; /* average |key| value */ node *hi=NULL, *lo=NULL; /* nodes not yet included in magic table */ register node *p, *q; /* pointer variables for list manipulation */ base_node=gb_typed_alloc(nn,node,temp_nodes); table=gb_typed_alloc(nn,magic_entry,g->aux_data); if (!gb_trouble_code) { @<Initialize the |hi| and |lo| lists@>; while (hi) @<Remove a |lo| element and match it with a |hi| element; deduct the residual probability from that |hi|~element@>; while (lo) @<Remove a |lo| element of |key| value |t|@>; } gb_free(temp_nodes); return table; /* if |gb_trouble_code| is nonzero, the table is empty */ } @ @<Initialize the |hi| and |lo| lists@>= t=0x40000000/nn; /* this division is exact */ p=base_node; while (nn>n) { p->key=0; p->link=lo; p->j=--nn; lo=p++; } for (dist=dist+n-1; n>0; dist--,p++) { p->key=*dist; p->j=--n; if (*dist>t) p->link=hi,@, hi=p; else p->link=lo,@, lo=p; } @ When we change the scale factor from $2^{30}$ to $2^{31}$, we need to be careful lest integer overflow occur. The introduction of register |x| into this code removes the risk. @<Remove a |lo| element and match it with a |hi| element...@>= {@+register magic_entry *r; register long x; p=hi,@, hi=p->link; q=lo,@, lo=q->link; r=table+q->j; x=t*q->j+q->key-1; r->prob=x+x+1; r->inx=p->j; /* we have just given |q->key| units of probability to vertex |q->j|, and |t-q->key| units to vertex |p->j| */ if ((p->key-=t-q->key)>t) p->link=hi,@, hi=p; else p->link=lo,@, lo=p; } @ When all remaining entries have the average probability, the |inx| component need not be set, because it will never be used. @<Remove a |lo| element of |key| value |t|@>= {@+register magic_entry *r; register long x; q=lo, lo=q->link; r=table+q->j; x=t*q->j+t-1; r->prob=x+x+1; /* that's |t| units of probability for vertex |q->j| */ } @*Random bipartite graphs. The procedure call $$\hbox{|random_bigraph(n1,n2,m,multi,dist1,dist2,min_len,max_len,seed)|}$$ is designed to produce a pseudo-random bipartite graph with |n1| vertices in one part and |n2| in the other, having |m| edges. The remaining parameters |multi|, |dist1|, |dist2|, |min_len|, |max_len|, and |seed| have the same meaning as the analogous parameters of |random_graph|. In fact, |random_bigraph| does its work by reducing its parameters to a special case of |random_graph|. Almost all that needs to be done is to pad |dist1| with |n2| trailing zeroes and |dist2| with |n1| leading zeroes. The only slightly tricky part occurs when |dist1| and/or |dist2| are null, since non-null distribution vectors summing exactly to $2^{30}$ must then be fabricated. @<External f...@>= Graph *random_bigraph(n1,n2,m,multi,dist1,dist2,min_len,max_len,seed) unsigned long n1,n2; /* number of vertices desired in each part */ unsigned long m; /* number of edges desired */ long multi; /* allow duplicate edges? */ long *dist1, *dist2; /* distribution of edge endpoints */ long min_len,max_len; /* bounds on random lengths */ long seed; /* random number seed */ {@+unsigned long n=n1+n2; /* total number of vertices */ Area new_dists; long *dist_from, *dist_to; Graph *new_graph; init_area(new_dists); if (n1==0 || n2==0) panic(bad_specs); /* illegal options */ if (min_len>max_len) panic(very_bad_specs); /* what are you trying to do? */ if (((unsigned long)(max_len))-((unsigned long)(min_len))>= ((unsigned long)0x80000000)) panic(bad_specs+1); /* too much range */ dist_from=gb_typed_alloc(n,long,new_dists); dist_to=gb_typed_alloc(n,long,new_dists); if (gb_trouble_code) { gb_free(new_dists); panic(no_room+2); /* no room for auxiliary distribution tables */ } @<Compute the entries of |dist_from| and |dist_to|@>; new_graph=random_graph(n,m,multi,0L,0L, dist_from,dist_to,min_len,max_len,seed); sprintf(new_graph->id,"random_bigraph(%lu,%lu,%lu,%d,%s,%s,%ld,%ld,%ld)",@| n1,n2,m,multi>0?1:multi<0?-1:0,dist_code(dist1),dist_code(dist2),@| min_len,max_len,seed); mark_bipartite(new_graph,n1); gb_free(new_dists); return new_graph; } @ The relevant identity we need here is the replicative law for the floor function: $$\left\lfloor x\over n\right\rfloor+\left\lfloor x+1\over n\right\rfloor + \cdots + \left\lfloor x+n-1\over n\right\rfloor = \lfloor x\rfloor\,.$$ @<Compute the entries...@>= {@+register long *p, *q; /* traversers of the dists */ register long k; /* vertex count */ p=dist1; q=dist_from; if (p) while (p<dist1+n1) *q++=*p++; else for (k=0; k<n1; k++) *q++=(0x40000000+k)/n1; p=dist2; q=dist_to+n1; if (p) while (p<dist2+n2) *q++=*p++; else for (k=0; k<n2; k++) *q++=(0x40000000+k)/n2; } @* Random lengths. The subroutine call $$\hbox{|random_lengths(g,directed,min_len,max_len,dist,seed)|}$$ takes an existing graph and assigns new lengths to each of its arcs. If |dist=NULL|, the lengths will be uniformly distributed between |min_len| and |max_len| inclusive; otherwise |dist| should be a probability distribution vector of length |max_len-min_len+1|, like those in |random_graph|. If |directed=0|, pairs of arcs $u\to v$ and $v\to u$ will be regarded as a single edge, both arcs receiving the same length. The procedure returns a nonzero value if something goes wrong; in that case, graph |g| will not have been changed. Alias tables for generating nonuniform random lengths will survive in |g->aux_data|. @<External f...@>= long random_lengths(g,directed,min_len,max_len,dist,seed) Graph *g; /* graph whose lengths will be randomized */ long directed; /* is it directed? */ long min_len,max_len; /* bounds on random lengths */ long *dist; /* distribution of lengths */ long seed; /* random number seed */ {@+register Vertex *u,*v; /* current vertices of interest */ register Arc *a; /* current arc of interest */ long nn=1, kk=31; /* variables for nonuniform generation */ magic_entry *dist_table; /* alias table for nonuniform generation */ if (g==NULL) return missing_operand; /* where is |g|? */ gb_init_rand(seed); if (min_len>max_len) return very_bad_specs; /* what are you trying to do? */ if (((unsigned long)(max_len))-((unsigned long)(min_len))>= ((unsigned long)0x80000000)) return bad_specs; /* too much range */ @<Check |dist| for validity, and set up the |dist_table|@>; sprintf(buffer,",%d,%ld,%ld,%s,%ld)",directed?1:0,@| min_len,max_len,dist_code(dist),seed); make_compound_id(g,"random_lengths(",g,buffer); @<Run through all arcs and assign new lengths@>; return 0; } @ @<Private dec...@>= static char buffer[]="1,-1000000001,-1000000000,dist,1000000000)"; @ @<Check |dist| for validity...@>= if (dist) {@+register long acc; /* sum of probabilities */ register long *p; /* pointer to current probability of interest */ register long n=max_len-min_len+1; for (acc=0,p=dist; p<dist+n; p++) { if (*p<0) return -1; /* negative probability */ if (*p>0x40000000-acc) return 1; /* probability too high */ acc+=*p; } if (acc!=0x40000000) return 2; /* probabilities don't sum to 1 */ while (nn<n) nn+=nn,kk--; dist_table=walker(n,nn,dist,g); if (gb_trouble_code) { gb_trouble_code=0; return alloc_fault; /* not enough room to generate the magic tables */ } } @ @<Run through all arcs and assign new lengths@>= for (u=g->vertices;u<g->vertices+g->n;u++) for (a=u->arcs;a;a=a->next) { v=a->tip; if (directed==0 && u>v) a->len=(a-1)->len; else {@+register long len; /* a random length */ if (dist==0) len=rand_len; else {@+long uu=gb_next_rand(); long k=uu>>kk; magic_entry *magic=dist_table+k; if (uu<=magic->prob) len=min_len+k; else len=min_len+magic->inx; } a->len=len; if (directed==0 && u==v && a->next==a+1) (++a)->len=len; } } @* Index. Here is a list that shows where the identifiers of this program are defined and used. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gb_roget.w������������������������������������������������������������������������������������������0000444�0001750�0001750�00000021450�05635233231�011162� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_\,ROGET} \prerequisites{GB\_\,GRAPH}{GB\_\,IO} @* Introduction. This GraphBase module contains the |roget| subroutine, which creates a family of graphs based on Roget's Thesaurus. An example of the use of this procedure can be found in the demo program {\sc ROGET\_\,COMPONENTS}. @(gb_roget.h@>= extern Graph *roget(); @ The subroutine call |roget(n,min_distance,prob,seed)| constructs a graph based on the information in \.{roget.dat}. Each vertex of the graph corresponds to one of the 1022 categories in the 1879 edition of Peter Mark Roget's {\sl Thesaurus of English Words @^Roget, Peter Mark@>@^Roget, John Lewis@> and Phrases}, edited by John Lewis Roget. An arc goes from one category to another if Roget gave a reference to the latter among the words and phrases of the former, or if the two categories were directly related to each other by their positions in Roget's book. For example, the vertex for category 312 (`ascent') has arcs to the vertices for categories 224 (`obliquity'), 313 (`descent'), and 316 (`leap'), because Roget gave explicit cross-references from 312 to 224 and~316, and because category 312 was implicitly paired with 313 in his scheme. The constructed graph will have $\min(n,1022)$ vertices; however, the default value |n=1022| is substituted when |n=0|. If |n| is less than 1022, the |n| categories will be selected at random, and all arcs to unselected categories will be omitted. Arcs will also be omitted if they correspond to categories whose numbers differ by less than |min_distance|. For example, if |min_distance>1|, the arc between categories 312 and~313 will not be included. (Roget sometimes formed clusters of three interrelated categories; to avoid cross-references within all such clusters, you can set |min_distance=3|.) If |prob>0|, arcs that would ordinarily be included in the graph are rejected with probability |prob/65536|. This provides a way to obtain sparser graphs. The vertices will appear in random order. However, all ``randomness'' in GraphBase graphs is reproducible; it depends only on the value of a given |seed|, which can be any nonnegative integer less than~$2^{31}$. For example, everyone who asks for |roget(1000,3,32768,50)| will obtain exactly the same graph, regardless of their computer system. Changing the value of |prob| will affect only the arcs of the generated graph; it will change neither the choice of vertices nor the vertex order. @d MAX_N 1022 /* the number of categories in Roget's book */ @ If the |roget| routine encounters a problem, it returns |NULL| (\.{NULL}), after putting a code number into the external variable |panic_code|. This code number identifies the type of failure. Otherwise |roget| 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_trouble_code=0;@+return NULL;@+} @ The \CEE/ file \.{gb\_roget.c} has the following general shape: @p #include "gb_io.h" /* we will use the {\sc GB\_\,IO} routines for input */ #include "gb_flip.h" /* we will use the {\sc GB\_\,FLIP} routines for random numbers */ #include "gb_graph.h" /* and we will use the {\sc GB\_\,GRAPH} data structures */ @h@# @<Private variables@>@; @# Graph *roget(n,min_distance,prob,seed) unsigned long n; /* number of vertices desired */ unsigned long min_distance; /* smallest inter-category distance allowed in an arc */ unsigned long prob; /* 65536 times the probability of rejecting an arc */ long seed; /* random number seed */ {@+@<Local variables@>@;@# gb_init_rand(seed); if (n==0 || n>MAX_N) n=MAX_N; @<Set up a graph with |n| vertices@>; @<Determine the |n| categories to use in the graph@>; @<Input \.{roget.dat} and build the graph@>; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } return new_graph; } @ @<Local var...@>= Graph *new_graph; /* the graph constructed by |roget| */ @* Vertices. @<Set up a graph with |n| vertices@>= 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,"roget(%lu,%lu,%lu,%ld)",n,min_distance,prob,seed); strcpy(new_graph->util_types,"IZZZZZZZZZZZZZ"); @ The first nontrivial thing we need to do is find a random selection and permutation of |n| vertices. We will compute a |mapping| table such that |mapping[k]| is non-|NULL| for exactly |n| randomly selected category numbers~|k|. Moreover, these non-|NULL| values will be a random permutation of the vertices of the graph. @<Priv...@>= static Vertex *mapping[MAX_N+1]; /* the vertex corresponding to a given category */ static long cats[MAX_N]; /* table of category numbers that have not yet been used */ @ During the loop on |v| in this step, |k| is the number of categories whose |mapping| value is still~|NULL|. The first |k| entries of |cats| will contain those category numbers in some order. @<Determine the |n| categories to use in the graph@>= for (k=0; k<MAX_N; k++) cats[k]=k+1,@,mapping[k+1]=NULL; for (v=new_graph->vertices+n-1; v>=new_graph->vertices; v--) { j=gb_unif_rand(k); mapping[cats[j]]=v; cats[j]=cats[--k]; } @ @<Local...@>= register long j,k; /* all-purpose indices */ register Vertex *v; /* current vertex */ @* Arcs. The data in \.{roget.dat} appears in 1022 lines, one for each category. For example, the line $$\hbox{\tt 312ascent:224 313 316}$$ specifies the arcs from category 312 as explained earlier. First comes the category number, then the category name, then a colon, then zero or more numbers specifying arcs to other categories; the numbers are separated by spaces. Some categories have too many arcs to fit on a single line; the data for these categories can be found on two lines, the first line ending with a backslash and the second line beginning with a space. @<Input \.{roget.dat} and build the graph@>= if (gb_open("roget.dat")!=0) panic(early_data_fault); /* couldn't open |"roget.dat"| using GraphBase conventions */ for (k=1; !gb_eof(); k++) @<Read the data for category |k|, and put it in the graph if it has been selected@>; if (gb_close()!=0) panic(late_data_fault); /* something's wrong with |"roget.dat"|; see |io_errors| */ if (k!=MAX_N+1) panic(impossible); /* we don't have the right value of |MAX_N| */ @ We check that the data isn't garbled, except that we don't bother to look at unselected categories. The original category number is stored in vertex utility field |cat_no|, in case anybody wants to see it. @d cat_no u.I /* utility field |u| of each vertex holds the category number */ @<Read the data for category |k|, and put it in the graph if it has been selected@>= { if (mapping[k]) { /* yes, this category has been selected */ if (gb_number(10)!=k) panic(syntax_error); /* out of synch */ (void)gb_string(str_buf,':'); if (gb_char()!=':') panic(syntax_error+1); /* no colon found */ v=mapping[k]; v->name=gb_save_string(str_buf); v->cat_no=k; @<Add arcs from |v| for every category that's both listed on the line and selected@>; }@+else @<Skip past the data for one category@>; } @ @(gb_roget.h@>= #define cat_no @t\quad@> u.I /* definition of |cat_no| is repeated in the header file */ @ @d iabs(x) ((x)<0? -(x): (x)) @<Add arcs from |v| for every...@>= j=gb_number(10); if (j==0) goto done; /* some categories lead to no arcs at all */ while (1) { if (j>MAX_N) panic(syntax_error+2); /* category code out of range */ if (mapping[j] && iabs(j-k)>=min_distance && (prob==0 || ((gb_next_rand()>>15)>=prob))) gb_new_arc(v,mapping[j],1L); switch (gb_char()) { case '\\': gb_newline(); if (gb_char()!=' ') panic(syntax_error+3); /* space should begin a continuation line */ /* fall through to the space case */ case ' ': j=gb_number(10);@+break; case '\n': goto done; default: panic(syntax_error+4); /* illegal character following category number */ } } done: gb_newline(); @ We want to call |gb_newline()| twice if the current line ends with a backslash; otherwise we want to call it just once. There's an obvious way to do that, and there's also a faster and trickier way. The author apologizes here for succumbing to some old-fashioned impulses. (Recall that |gb_string| returns the location just following the |'\0'| it places at the end of a scanned string.) @<Skip past the data for one category@>= { if (*(gb_string(str_buf,'\n')-2)=='\\') gb_newline(); /* the first line ended with backslash */ gb_newline(); } @* Index. Here is a list that shows where the identifiers of this program are defined and used. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gb_words.w������������������������������������������������������������������������������������������0000444�0001750�0001750�00000052345�05635233312�011207� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_WORDS} \font\logosl=logosl10 \prerequisites{GB\_\,GRAPH}{GB\_\,IO} @* Introduction. This GraphBase module provides two external subroutines: $$\vcenter{\halign{#\hfil\cr |words|, a routine that creates a graph based on five-letter words;\cr |find_word|, a routine that looks for a given vertex in such a graph.\cr}}$$ Examples of the use of these routines can be found in two demo programs, {\sc WORD\_\,COMPONENTS} and {\sc LADDERS}. @(gb_words.h@>= extern Graph *words(); extern Vertex *find_word(); @ The subroutine call |words(n,wt_vector,wt_threshold,seed)| constructs a graph based on the five-letter words in \.{words.dat}. Each vertex of the graph corresponds to a single five-letter word. Two words are adjacent in the graph if they are the same except in one letter position. For example, `\.{words}' is adjacent to other words such as `\.{cords}', `\.{wards}', `\.{woods}', `\.{worms}', and `\.{wordy}'. The constructed graph has at most |n| vertices; indeed, it has exactly |n| vertices if there are enough qualifying words. A word qualifies if its ``weight'' is |wt_threshold| or more, when weights are computed from a table pointed to by~|wt_vector| according to rules described below. (If parameter~|wt_vector| is |NULL|, i.e., \.{NULL}, default weights are used.) The fourth parameter, |seed|, is the seed of a random number generator. All words of \.{words.dat} will be sorted by weight. The first vertex of the graph will be the word of largest weight, the second vertex will have second-largest weight, and so on. Words of equal weight will appear in pseudo-random order, as determined by the value of |seed| in a system-independent fashion. The first |n| words in order of decreasing weight are chosen to be vertices of the graph. However, if fewer than |n| words have weight |>= wt_threshold|, the graph will contain only the words that qualify. In such cases the graph will have fewer than |n| vertices---possibly none at all. Exception: The special case |n=0| is equivalent to the case when |n| has been set to the highest possible value. It causes all qualifying words to appear. @ Every word in \.{words.dat} has been classified as `common' (\.*), `advanced' (\.+), or `unusual' (\.\ ). Each word has also been assigned seven frequency counts $c_1$, \dots,~$c_7$, separated by commas; these counts show how often the word has occurred in different publication contexts: $$\vcenter{\halign{$c_#$ times in &#\hfil\cr 1&the American Heritage Intermediate Corpus of elementary school material;\cr 2&the Brown Corpus of reading material from America;\cr 3&the Lancaster-Oslo/Bergen Corpus of reading material from Britain;\cr 4&the Melbourne-Surrey Corpus of newspaper material from Australia;\cr 5&the Revised Standard Version of the Bible;\cr 6&{\sl The \TEX/book\/} and {\sl The {\logosl METAFONT\kern1pt}book\/} by D. E. Knuth;\cr 7&{\sl Concrete Mathematics\/} by Graham, Knuth, and Patashnik.\cr}}$$ @^Graham, Ronald Lewis@> @^Knuth, Donald Ervin@> @^Patashnik, Oren@> For example, one of the entries in \.{words.dat} is $$\.{happy*774,92,121,2,26,8,1}$$ indicating a common word with $c_1=774$, \dots, $c_7=1$. Parameter |wt_vector| points to an array of nine integers $(a,b,w_1,\ldots,w_7)$. The weight of each word is computed from these nine numbers by using the formula $$c_1w_1+\cdots+c_7w_7+ \cases{a,&if the word is `common';\cr b,&if the word is `advanced';\cr 0,&if the word is `unusual'.\cr}$$ The components of |wt_vector| must be chosen so that $$\max\bigl(\vert a\vert, \vert b\vert\bigr) + C_1\vert w_1\vert + \cdots +C_7\vert w_7\vert < 2^{30},$$ where $C_j$ is the maximum value of $c_j$ in the file; this restriction ensures that the |words| procedure will produce the same results on all computer systems. @ The maximum frequency counts actually present are $C_1=15194$, $C_2=3560$, $C_3=4467$, $C_4=460$, $C_5=6976$, $C_6=756$, and $C_7=362$; these can be found in the entries for the common words `\.{shall}', `\.{there}', `\.{which}', and `\.{would}'. The default weights are $a=100$, $b=10$, $c_1=4$, $c_2=c_3=2$, $c_4=c_5= c_6=c_7=1$. File \.{words.dat} contains 5757 words, of which 3300 are `common', 1194 are `advanced', and 1263 are `unusual'. Included among the unusual words are 891 having $c_1=\cdots=c_7=0$; such words will always have weight zero, regardless of the weight vector parameter. @<Private variables@>= static long max_c[]={15194,3560,4467,460,6976,756,362}; /* maximum counts $C_j$ */ static long default_wt_vector[]={100,10,4,2,2,1,1,1,1}; /* use this if |wt_vector=NULL| */ @ Examples: If you call |words(2000,NULL,0,0)|, you get a graph with 2000 of the most common five-letter words of English, using the default weights. The GraphBase programs are designed to be system-independent, so that identical graphs will be obtained by everybody who asks for |words(2000,NULL,0,0)|. Equivalent experiments on algorithms for graph manipulation can therefore be performed by researchers in different parts of the world. The subroutine call |words(2000,NULL,0,s)| will produce slightly different graphs when the random seed |s| varies, because some words have equal weight. However, the graph for any particular value of~|s| will be the same on all computers. The seed value can be any integer in the range $0\le s<2^{31}$. Suppose you call |words(0,w,1,0)|, with |w| defined by the \CEE/ declaration $$\hbox{|long w[9] = {1};|}$$ this means that $a=1$ and $b=w_1=\cdots=w_7=0$. Therefore you'll get a graph containing only the 3300 `common' words. Similarly, it's possible to obtain only the $3300+1194=4494$ non-`unusual' words, by specifying the weight vector $$\hbox{|long w[9] = {1,1};|}$$ this makes $a=b=1$ and $w_1=\cdots=w_7=0$. In both of these examples, the qualifying words all have weight~1, so the vertices of the graph will appear in pseudo-random order. If |w| points to an array of nine 0's, the call |words(n,w,0,s)| gives a random sample of |n| words, depending on |s| in a system-independent fashion. If the entries of the weight vector are all nonnegative, and if the weight threshold is zero, every word of \.{words.dat} will qualify. Thus you will obtain a graph with $\min(n,5757)$ vertices. If |w| points to an array with {\sl negative\/} weights, the call |words(n,w,-0x7fffffff,0)| selects |n| of the {\sl least\/} common words in \.{words.dat}. @ If the |words| routine encounters a problem, it returns |NULL|, after putting a code number into the external variable |panic_code|. This code number identifies the type of failure. Otherwise |words| 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) @+{@+gb_free(node_blocks); panic_code=c;@+gb_trouble_code=0;@+return NULL;@+} @ Now let's get going on the program. The \CEE/ file \.{gb\_words.c} begins as follows: @p #include "gb_io.h" /* we will use the {\sc GB\_\,IO} routines for input */ #include "gb_flip.h" /* we will use the {\sc GB\_\,FLIP} routines for random numbers */ #include "gb_graph.h" /* we will use the {\sc GB\_\,GRAPH} data structures */ #include "gb_sort.h" /* and |gb_linksort| for sorting */ @h@# @<Type declarations@>@; @<Private variables@>@; @<Private functions@>@; @# Graph *words(n,wt_vector,wt_threshold,seed) unsigned long n; /* maximum number of vertices desired */ long wt_vector[]; /* pointer to array of weights */ long wt_threshold; /* minimum qualifying weight */ long seed; /* random number seed */ {@+@<Local variables@>@;@# gb_init_rand(seed); @<Check that |wt_vector| is valid@>; @<Input the qualifying words to a linked list, computing their weights@>; @<Sort and output the words, determining adjacencies@>; if (gb_trouble_code) { gb_recycle(new_graph); panic(alloc_fault); /* oops, we ran out of memory somewhere back there */ } return new_graph; } @ @<Local var...@>= Graph *new_graph; /* the graph constructed by |words| */ @* Validating the weights. The first job that |words| needs to tackle is comparatively trivial: We want to verify the condition $$\max\bigl(\vert a\vert, \vert b\vert\bigr) + C_1\vert w_1\vert + \cdots +C_7\vert w_7\vert < 2^{30}.\eqno(*)$$ This proves to be an interesting exercise in ``portable \CEE/ programming,'' because we don't want to risk integer overflow. Our approach is to do the calculation first in floating point arithmetic, thereby ruling out cases that are clearly unacceptable. Once that test is passed, we can safely test the condition with ordinary integer arithmetic. Floating point arithmetic is system dependent, but we use it carefully so that system-independent results are obtained. @<Check that |wt_vector| is valid@>= if (!wt_vector) wt_vector=default_wt_vector; else {@+register double flacc; register long *p,*q; register long acc; @<Use floating point arithmetic to check that |wt_vector| isn't totally off base@>; @<Use integer arithmetic to check that |wt_vector| is truly OK@>; } @ The floating-point calculations are facilitated by a routine that converts an integer to its absolute value, expressed as a |double|: @<Private functions@>= static double flabs(x) long x; {@+if (x>=0) return (double)x; return -((double)x); } @ Although floating point arithmetic is system dependent, we can certainly assume that at least 16 bits of precision are used. This implies that the difference between |flabs(x)| and $\vert x\vert$ must be less than $2^{14}$. Also, if $x$ and $y$ are nonnegative values less than $2^{31}$, the difference between their floating-point sum and their true sum must be less than $2^{14}$. The floating point calculations in the following test will never reject a valid weight vector. For if condition $(*)$ holds, the floating-point value of $\max(\hbox{|flabs(a)|},\hbox{|flabs(b)|})+C_1*|flabs|(w_1)+\cdots +C_7*|flabs|(w_7)$ will be less than $2^{30}+(8+C_1+\cdots+C_7)2^{14}$, which is less than $2^{30}+2^{29}$. @<Use float...@>= p=wt_vector; flacc=flabs(*p++); if (flacc<flabs(*p)) flacc=flabs(*p); /* now $|flacc|=\max(\vert a\vert,\vert b\vert)$ */ for (q=&max_c[0]; q<&max_c[7]; q++) flacc += *q * flabs(*++p); if (flacc>=(double)0x60000000) /* this constant is $6\times2^{28}=2^{30}+2^{29}$ */ panic(very_bad_specs); /* whoa; the weight vector is way too big */ @ Conversely, if the floating point test just made is passed, the true value of the sum will be less than $2^{30}+2^{29}+2^{29}=2^{31}$; hence integer overflow will never occur when we make the following more refined test: @<Use int...@>= p=wt_vector; acc=iabs(*p++); if (acc<iabs(*p)) acc=iabs(*p); /* now $|acc|=\max(\vert a\vert,\vert b\vert)$ */ for (q=&max_c[0]; q<&max_c[7]; q++) acc += *q * iabs(*++p); if (acc>=0x40000000) panic(bad_specs); /* the weight vector is a bit too big */ @ @<Private f...@>= static long iabs(x) long x; {@+if (x>=0) return (long)x; return -((long)x); } @* The input phase. Now we're ready to read \.{words.dat}. @<Local...@>= register long wt; /* the weight of the current word */ char word[5]; /* the current five-letter word */ long nn=0; /* the number of qualifying words found so far */ @ As we read the words, we will form a linked list of nodes containing each qualifying word and its weight, using the memory management routines of {\sc GB\_\,GRAPH} to allocate space for 111 nodes at a time. These nodes should be returned to available memory later, so we will keep them in a separate area under local control. The nodes start out with |key| and |link| fields, as required by the |gb_linksort| routine, which we'll use to sort by weight. The sort key must be nonnegative; we obtain it by adding $2^{30}$ to the weight. @d nodes_per_block 111 @<Type...@>= typedef struct node_struct { long key; /* the sort key (weight plus $2^{30}$) */ struct node_struct *link; /* links the nodes together */ char wd[5]; /* five-letter word (which typically consumes eight bytes, too bad) */ } node; @ @<Local...@>= node *next_node; /* the next node available for allocation */ node *bad_node; /* if |next_node=bad_node|, the node isn't really there */ node *stack_ptr; /* the most recently created node */ node *cur_node; /* current node being created or examined */ @ @<Private v...@>= static Area node_blocks; /* the memory area for blocks of nodes */ @ @<Input the qualifying words...@>= next_node=bad_node=stack_ptr=NULL; if (gb_open("words.dat")!=0) panic(early_data_fault); /* couldn't open |"words.dat"| using GraphBase conventions; |io_errors| tells why */ do @<Read one word, and put it on the stack if it qualifies@>@; while (!gb_eof()); if (gb_close()!=0) panic(late_data_fault); /* something's wrong with |"words.dat"|; see |io_errors| */ @ @<Read one...@>= {@+register long j; /* position in |word| */ for (j=0; j<5; j++) word[j]=gb_char(); @<Compute the weight |wt|@>; if (wt>=wt_threshold) { /* it qualifies */ @<Install |word| and |wt| in a new node@>; nn++; } gb_newline(); } @ @d copy5(y,x) {@+ *(y)=*(x);@+ *((y)+1)=*((x)+1);@+ *((y)+2)=*((x)+2); *((y)+3)=*((x)+3);@+ *((y)+4)=*((x)+4);@+ } @<Install...@>= if (next_node==bad_node) { cur_node=gb_typed_alloc(nodes_per_block,node,node_blocks); if (cur_node==NULL) panic(no_room+1); /* out of memory already */ next_node=cur_node+1; bad_node=cur_node+nodes_per_block; }@+else cur_node=next_node++; cur_node->key=wt+0x40000000; cur_node->link=stack_ptr; copy5(cur_node->wd,word); stack_ptr=cur_node; @ Recall that |gb_number()| returns 0, without giving an error, if no digit is present in the current position of the file being read. This implies that the \.{words.dat} file need not include zero counts explicitly. Furthermore, we can arrange things so that trailing zero counts are unnecessary; commas can be omitted if all counts following them on the current line are zero. @<Compute the weight...@>= {@+register long *p,*q; /* pointers to $C_j$ and $w_j$ */ register long c; /* current count */ switch (gb_char()) { case '*': wt=wt_vector[0];@+break; /* `common' word */ case '+': wt=wt_vector[1];@+break; /* `advanced' word */ case ' ': case'\n': wt=0;@+break; /* `unusual' word */ default: panic(syntax_error); /* unknown type of word */ } p=&max_c[0]; q=&wt_vector[2]; do@+{ if (p==&max_c[7]) panic(syntax_error+1); /* too many counts */ c=gb_number(10); if (c>*p++) panic(syntax_error+2); /* count too large */ wt += c * *q++; }@+while (gb_char()==','); } @* The output phase. Once the input phase has examined all of \.{words.dat}, we are left with a stack of |nn| nodes containing the qualifying words, starting at |stack_ptr|. The next step is to call |gb_linksort|, which takes the qualifying words and distributes them into the 128 lists |gb_sorted[j]|, for |0<=j<128|. We can then access the words in order of decreasing weight by reading through these lists, starting with |gb_sorted[127]| and ending with |gb_sorted[0]|. (See the documentation of |gb_linksort| in the {\sc GB\_\,SORT} module.) The output phase therefore has the following general outline: @<Sort and output...@>= gb_linksort(stack_ptr); @<Allocate storage for the new graph; adjust |n| if it is zero or too large@>; if (gb_trouble_code==0 && n) { register long j; /* runs through sorted lists */ register node *p; /* the current node being output */ nn=n; for (j=127; j>=0; j--) for (p=(node*)gb_sorted[j]; p; p=p->link) { @<Add the word |p->wd| to the graph@>; if (--nn==0) goto done; } } done:gb_free(node_blocks); @ The only slightly unusual data structure needed is a set of five hash tables, one for each of the strings of four letters obtained by suppressing a single letter of a five-letter word. For example, a word like `\.{words}' will lead to entries for `\.{\ ords}', `\.{w\ rds}, `\.{wo\ ds}', `\.{wor\ s}', and `\.{word\ }', one in each of the hash tables. @d hash_prime 6997 /* a prime number larger than the total number of words */ @<Type...@>= typedef Vertex *hash_table[hash_prime]; @ @<Local...@>= Vertex *cur_vertex; /* the current vertex being created or examined */ char *next_string; /* where we'll store the next five-letter word */ @ @<Private v...@>= static hash_table *htab; /* five dynamically allocated hash tables */ @ The weight of each word will be stored in the utility field |u.I| of its |Vertex| record. The position in which adjacent words differ will be stored in utility field |a.I| of the |Arc| records between them. @d weight u.I /* weighted frequencies */ @d loc a.I /* index of difference (0, 1, 2, 3, or 4) */ @(gb_words.h@>= #define weight @[u.I@] /* repeat the definitions in the header file */ #define loc @[a.I@] @ @<Allocate storage for the new graph...@>= if (n==0 || nn<n) n=nn; new_graph=gb_new_graph(n); if (new_graph==NULL) panic(no_room); /* out of memory before we're even started */ if (wt_vector==default_wt_vector) sprintf(new_graph->id,"words(%lu,0,%ld,%ld)",n,wt_threshold,seed); else sprintf(new_graph->id, "words(%lu,{%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld},%ld,%ld)", n,wt_vector[0],wt_vector[1],wt_vector[2],wt_vector[3],wt_vector[4], wt_vector[5],wt_vector[6],wt_vector[7],wt_vector[8],wt_threshold,seed); strcpy(new_graph->util_types,"IZZZZZIZZZZZZZ"); cur_vertex=new_graph->vertices; next_string=gb_typed_alloc(6*n,char,new_graph->data); htab=gb_typed_alloc(5,hash_table,new_graph->aux_data); @ @<Add the word...@>= {@+register char *q; /* the new word */ q=cur_vertex->name=next_string; next_string+=6; copy5(q,p->wd); cur_vertex->weight=p->key-0x40000000; @<Add edges for all previous words |r| that nearly match |q|@>; cur_vertex++; } @ The length of each edge in a |words| graph is set to~1; the calling routine can change it later if desired. @d mtch(i) (*(q+i)==*(r+i)) @d match(a,b,c,d) (mtch(a)&&mtch(b)&&mtch(c)&&mtch(d)) @d store_loc_of_diff(k) cur_vertex->arcs->loc=(cur_vertex->arcs-1)->loc=k @d ch(q) ((long)*(q)) @d hdown(k) h==htab[k]? h=htab[k+1]-1: h-- @<Add edges for all previous words |r| that nearly match |q|@>= {@+register char *r; /* previous word possibly adjacent to |q| */ register Vertex **h; /* hash address for linear probing */ register long raw_hash; /* five-letter hash code before remaindering */ raw_hash=(((((((ch(q)<<5)+ch(q+1))<<5)+ch(q+2))<<5)+ch(q+3))<<5)+ch(q+4); for (h=htab[0]+(raw_hash-(ch(q)<<20)) % hash_prime; *h; hdown(0)) { r=(*h)->name; if (match(1,2,3,4)) gb_new_edge(cur_vertex,*h,1L), store_loc_of_diff(0); } *h=cur_vertex; for (h=htab[1]+(raw_hash-(ch(q+1)<<15)) % hash_prime; *h; hdown(1)) { r=(*h)->name; if (match(0,2,3,4)) gb_new_edge(cur_vertex,*h,1L), store_loc_of_diff(1); } *h=cur_vertex; for (h=htab[2]+(raw_hash-(ch(q+2)<<10)) % hash_prime; *h; hdown(2)) { r=(*h)->name; if (match(0,1,3,4)) gb_new_edge(cur_vertex,*h,1L), store_loc_of_diff(2); } *h=cur_vertex; for (h=htab[3]+(raw_hash-(ch(q+3)<<5)) % hash_prime; *h; hdown(3)) { r=(*h)->name; if (match(0,1,2,4)) gb_new_edge(cur_vertex,*h,1L), store_loc_of_diff(3); } *h=cur_vertex; for (h=htab[4]+(raw_hash-ch(q+4)) % hash_prime; *h; hdown(4)) { r=(*h)->name; if (match(0,1,2,3)) gb_new_edge(cur_vertex,*h,1L), store_loc_of_diff(4); } *h=cur_vertex; } @* Finding a word. After |words| has created a graph |g|, the user can remove the hash tables by calling |gb_free(g->aux_data)|. But if the hash tables have not been removed, another procedure can be used to find vertices that match or nearly match a given word. The subroutine call |find_word(q,f)| will return a pointer to a vertex that matches a given five-letter word~|q|, if that word is in the graph; otherwise, it returns |NULL| (i.e., \.{NULL}), after calling |f(v)| for each vertex~|v| whose word matches |q| in all but one letter position. @p Vertex *find_word(q,f) char *q; void @[@] (*f)(); /* |*f| should take one argument, of type |Vertex *|, or |f| should be |NULL| */ {@+register char *r; /* previous word possibly adjacent to |q| */ register Vertex **h; /* hash address for linear probing */ register long raw_hash; /* five-letter hash code before remaindering */ raw_hash=(((((((ch(q)<<5)+ch(q+1))<<5)+ch(q+2))<<5)+ch(q+3))<<5)+ch(q+4); for (h=htab[0]+(raw_hash-(ch(q)<<20)) % hash_prime; *h; hdown(0)) { r=(*h)->name; if (mtch(0) && match(1,2,3,4)) return *h; } @<Invoke |f| on every vertex that is adjacent to word~|q|@>; return NULL; } @ @<Invoke |f| on every vertex that is adjacent to word~|q|@>= if (f) { for (h=htab[0]+(raw_hash-(ch(q)<<20)) % hash_prime; *h; hdown(0)) { r=(*h)->name; if (match(1,2,3,4)) (*f)(*h); } for (h=htab[1]+(raw_hash-(ch(q+1)<<15)) % hash_prime; *h; hdown(1)) { r=(*h)->name; if (match(0,2,3,4)) (*f)(*h); } for (h=htab[2]+(raw_hash-(ch(q+2)<<10)) % hash_prime; *h; hdown(2)) { r=(*h)->name; if (match(0,1,3,4)) (*f)(*h); } for (h=htab[3]+(raw_hash-(ch(q+3)<<5)) % hash_prime; *h; hdown(3)) { r=(*h)->name; if (match(0,1,2,4)) (*f)(*h); } for (h=htab[4]+(raw_hash-ch(q+4)) % hash_prime; *h; hdown(4)) { r=(*h)->name; if (match(0,1,2,3)) (*f)(*h); } } @* Index. Here is a list that shows where the identifiers of this program are defined and used. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������assign_lisa.w���������������������������������������������������������������������������������������0000444�0001750�0001750�00000073217�07461722026�011702� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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 % PostScript is a registered trade mark of Adobe Systems Incorporated. \def\title{ASSIGN\_\,LISA} \def\<#1>{$\langle${\rm#1}$\rangle$} \def\dash{\mathrel-\joinrel\joinrel\mathrel-} % adjacent vertices \def\ddash{\mathrel{\above.2ex\hbox to1.1em{}}} % matched vertices @s compl normal @q unreserve a C++ keyword @> \prerequisite{GB\_\,LISA} @* The assignment problem. This demonstration program takes a matrix of numbers constructed by the {\sc GB\_\,LISA} module and chooses at most one number from each row and column in such a way as to maximize the sum of the numbers chosen. It also reports the number of ``mems'' (memory references) expended during its computations, so that the algorithm it uses can be compared with alternative procedures. The matrix has $m$ rows and $n$ columns. If $m\le n$, one number will be chosen in each row; if $m\ge n$, one number will be chosen in each column. The numbers in the matrix are brightness levels (pixel values) in a digitized version of the Mona Lisa. Of course the author does not pretend that the location of ``highlights'' in da Vinci's painting, one per row and one per column, has any application to art appreciation. However, this program does seem to have pedagogic value, because the relation between pixel values and shades of gray allows us to visualize the data underlying this special case of the assignment problem; ordinary matrices of numeric data are much harder to perceive. The nonrandom nature of pixels in a work of art might also have similarities to the ``organic'' properties of data in real-world applications. This program is optionally able to produce an encapsulated PostScript file from which the solution can be displayed graphically, with halftone shading. @ As explained in {\sc GB\_\,LISA}, the subroutine call |lisa(m,n,d,m0,m1,n0,n1,d0,d1,@[@t\\{area}@>@])| constructs an $m\times n$ matrix of integers between $0$ and~$d$, inclusive, based on the brightness levels in a rectangular region of a digitized Mona Lisa, where |m0|, |m1|, |n0|, and |n1| define that region. The raw data is obtained as a sum of |(m1-m0)(n1-n0)| pixel values between $0$ and~$255$, then scaled in such a way that sums |<=d0| are mapped to zero, sums |>=d1| are mapped to~$d$, and intermediate sums are mapped linearly to intermediate values. Default values |m1=360|, |n1=250|, |m=m1-m0|, |n=n1-n0|, |d=255|, and |d1=255(m1-m0)(n1-n0)| are substituted if any of the parameters |m|, |n|, |d|, |m1|, |n1|, or |d1| are zero. The user can specify the nine parameters |(m,n,d,m0,m1,n0,n1,d0,d1)| on the command line, at least in a \UNIX/ implementation, thereby obtaining a variety of special effects; the relevant command-line options are \.{m=}\<number>, \.{m0=}\<number>, and so on, with no spaces before or after the \.= signs that separate parameter names from parameter values. Additional options are also provided: \.{-s} (use only Mona Lisa's $16\times32$ ``smile''); \.{-e} (use only her $20\times50$ eyes); \.{-c} (complement black/white); \.{-p} (print the matrix and solution); \.{-P} (produce a PostScript file \.{lisa.eps} for graphic output); \.{-h} (use a heuristic that applies only when $m=n$); and \.{-v} or \.{-V} (print verbose or Very verbose commentary about the algorithm's performance). @^UNIX dependencies@> Here is the overall layout of this \CEE/ program: @p #include "gb_graph.h" /* the GraphBase data structures */ #include "gb_lisa.h" /* the |lisa| routine */ @h@# @<Global variables@>@; main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ {@+@<Local variables@>@;@# @<Scan the command-line options@>; mtx=lisa(m,n,d,m0,m1,n0,n1,d0,d1,working_storage); if (mtx==NULL) { fprintf(stderr,"Sorry, can't create the matrix! (error code %ld)\n", panic_code); return -1; } printf("Assignment problem for %s%s\n",lisa_id,(compl?", complemented":"")); sscanf(lisa_id,"lisa(%lu,%lu,%lu",&m,&n,&d); /* adjust for defaults */ if (m!=n) heur=0; if (printing) @<Display the input matrix@>; if (PostScript) @<Output the input matrix in PostScript format@>; mems=0; @<Solve the assignment problem@>; if (printing) @<Display the solution@>; if (PostScript) @<Output the solution in PostScript format@>; printf("Solved in %ld mems%s.\n",mems, (heur?" with square-matrix heuristic":"")); return 0; /* normal exit */ } @ @<Glob...@>= Area working_storage; /* where to put the input data and auxiliary arrays */ long *mtx; /* input data for the assignment problem */ long mems; /* the number of memory references counted while solving the problem */ @ The following local variables are related to the command-line options: @<Local v...@>= unsigned long m=0,n=0; /* number of rows and columns desired */ unsigned long d=0; /* number of pixel values desired, minus~1 */ unsigned long m0=0,m1=0; /* input will be from rows $[|m0|\,.\,.\,|m1|)$ */ unsigned long n0=0,n1=0; /* and from columns $[|n0|\,.\,.\,|n1|)$ */ unsigned long d0=0,d1=0; /* lower and upper threshold of raw pixel scores */ long compl=0; /* should the input values be complemented? */ long heur=0; /* should the square-matrix heuristic be used? */ long printing=0; /* should the input matrix and solution be printed? */ long PostScript=0; /* should an encapsulated PostScript file be produced? */ @ @<Scan the command-line options@>= while (--argc) { @^UNIX dependencies@> if (sscanf(argv[argc],"m=%lu",&m)==1) ; else if (sscanf(argv[argc],"n=%lu",&n)==1) ; else if (sscanf(argv[argc],"d=%lu",&d)==1) ; else if (sscanf(argv[argc],"m0=%lu",&m0)==1) ; else if (sscanf(argv[argc],"m1=%lu",&m1)==1) ; else if (sscanf(argv[argc],"n0=%lu",&n0)==1) ; else if (sscanf(argv[argc],"n1=%lu",&n1)==1) ; else if (sscanf(argv[argc],"d0=%lu",&d0)==1) ; else if (sscanf(argv[argc],"d1=%lu",&d1)==1) ; else if (strcmp(argv[argc],"-s")==0) { smile; /* sets |m0|, |m1|, |n0|, |n1| */ d1=100000; /* makes the pixels brighter */ } else if (strcmp(argv[argc],"-e")==0) { eyes; d1=200000; } else if (strcmp(argv[argc],"-c")==0) compl=1; else if (strcmp(argv[argc],"-h")==0) heur=1; else if (strcmp(argv[argc],"-v")==0) verbose=1; else if (strcmp(argv[argc],"-V")==0) verbose=2; /* terrifically verbose */ else if (strcmp(argv[argc],"-p")==0) printing=1; else if (strcmp(argv[argc],"-P")==0) PostScript=1; else { fprintf(stderr, "Usage: %s [param=value] [-s] [-c] [-h] [-v] [-p] [-P]\n",argv[0]); return -2; } } @ @<Display the input matrix@>= for (k=0;k<m;k++) { for (l=0;l<n;l++) printf("% 4ld",compl?d-*(mtx+k*n+l):*(mtx+k*n+l)); printf("\n"); } @ We obtain a crude but useful estimate of the computation time by counting mem units, as explained in the {\sc MILES\_\,SPAN} program. @d o mems++ @d oo mems+=2 @d ooo mems+=3 @* Algorithmic overview. The assignment problem is the classical problem of weighted bipartite matching: to choose a maximum-weight set of disjoint edges in a bipartite graph. We will consider only the case of complete bipartite graphs, when the weights are specified by an $m\times n$ matrix. An algorithm is most easily developed if we begin with the assumption that the matrix is square (i.e., that $m=n$), and if we change from maximization to minimization. Then the assignment problem is the task of finding a permutation $\pi[0]\ldots\pi[n-1]$ of $\{0,\ldots,n-1\}$ such that $\sum_{k=0}^{n-1} a_{k\pi[k]}$ is minimized, where $A=(a_{kl})$ is a given matrix of numbers $a_{kl}$ for $0\le k,l<n$. The algorithm below works for arbitrary real numbers $a_{kl}$, but we will assume in our implementation that the matrix entries are integers. One way to approach the assignment problem is to make three simple observations: (a)~Adding a constant to any row of the matrix does not change the solution $\pi[0]\ldots\pi[n-1]$. (b)~Adding a constant to any column of the matrix does not change the solution. (c)~If $a_{kl}\ge0$ for all $k$ and~$l$, and if $\pi[0]\ldots\pi[n-1]$ is a permutation with the property that $a_{k\pi[k]}=0$ for all~$k$, then $\pi[0]\ldots\pi[n-1]$ solves the assignment problem. The remarkable fact is that these three observations actually suffice. In other words, there is always a sequence of constants $(\sigma_0,\ldots,\sigma_ {n-1})$ and $(\tau_0,\ldots,\tau_{n-1})$ and a permutation $\pi[0]\ldots \pi[n-1]$ such that $$\vbox{\halign{$#$,\hfil&\quad for #\hfil\cr a_{kl}-\sigma_k+\tau_{\,l}\ge0& $0\le k<n$ and $0\le l<n$;\cr a_{k\pi[k]}-\sigma_k+\tau_{\pi[k]}=0& $0\le k<n$.\cr}}$$ @ To prove the remarkable fact just stated, we start by reviewing the theory of {\sl unweighted\/} bipartite matching. Any $m\times n$ matrix $A=(a_{kl})$ defines a bipartite graph on the vertices $(r_0,\ldots,r_{m-1})$ and $(c_0,\ldots,c_{n-1})$ if we say that $r_k\dash c_l$ whenever $a_{kl}=0$; in other words, the edges of the bipartite graph are the zeroes of the matrix. Two zeroes of~$A$ are called {\sl independent\/} if they appear in different rows and columns; this means that the corresponding edges have no vertices in common. A set of mutually independent zeroes of the matrix therefore corresponds to a set of mutually disjoint edges, also called a {\sl matching\/} between rows and columns. The Hungarian mathematicians Egerv\'ary and K\H{o}nig proved @^Egerv\'ary, Eugen (= Jen\H{o})@> @:Konig}{K\H{o}nig, D\'enes@> [{\sl Matematikai \'es Fizikai Lapok\/ \bf38} (1931), 16--28, 116--119] that the maximum number of independent zeroes in a matrix is equal to the minimum number of rows and/or columns that are needed to ``cover'' every zero. In other words, if we can find $p$ independent zeroes but not~$p+1$, then there is a way to choose $p$ lines in such a way that every zero of the matrix is included in at least one of the chosen lines, where a ``line'' is either a row or a column. Their proof was constructive, and it leads to a useful computer algorithm. Given a set of $p$ independent zeroes of a matrix, let us write $r_k\ddash c_l$ or $c_l\ddash r_k$ and say that $r_k$ is matched with $c_l$ if $a_{kl}$ is one of these $p$ special zeroes, while we continue to write $r_k\dash c_l$ or $c_l\dash r_k$ if $a_{kl}$ is one of the nonspecial zeroes. A given set of $p$ special zeroes defines a choice of $p$ lines in the following way: Column~$c$ is chosen if and only if it is reachable by a path of the form $$r^{(0)}\dash c^{(1)}\ddash r^{(1)}\dash c^{(2)}\ddash\cdots \dash c^{(q)}\ddash r^{(q)}\,,\eqno(*)$$ where $r^{(0)}$ is unmatched, $q\ge1$, and $c=c^{(q)}$. Row~$r$ is chosen if and only if it is matched with a column that is not chosen. Thus exactly $p$ lines are chosen. We can now prove that the chosen lines cover all the zeroes, unless there is a way to find $p+1$ independent zeroes. For if $c\ddash r$, either $c$ or $r$ has been chosen. And if $c\dash r$, one of the following cases must arise. (1)~If $r$ and~$c$ are both unmatched, we can increase~$p$ by matching them to each other. (2)~If $r$ is unmatched and $c\ddash r'$, then $c$ has been chosen, so the zero has been covered. (3)~If $r$ is matched to $c'\ne c$, then either $r$ has been chosen or $c'$ has been chosen. In the latter case, there is a path of the form $$r^{(0)}\dash c^{(1)}\ddash r^{(1)}\dash c^{(2)}\ddash\cdots\ddash r^{(q-1)}\dash c'\ddash r\dash c\,,$$ where $r^{(0)}$ is unmatched and $q\ge1$. If $c$ is matched, it has therefore been chosen; otherwise we can increase $p$ by redefining the matching to include $$r^{(0)}\ddash c^{(1)}\dash r^{(1)}\ddash c^{(2)}\dash\cdots\dash r^{(q-1)}\ddash c'\dash r\ddash c\,.$$ @ Now suppose $A$ is a {\sl nonnegative\/} matrix, of size $n\times n$. Cover the zeroes of~$A$ with a minimum number of lines, $p$, using the algorithm of Egerv\'ary and K\H{o}nig. If $p<n$, some elements are still uncovered, so those elements are positive. Suppose the minimum uncovered value is $\delta>0$. Then we can subtract $\delta$ from each unchosen row and add $\delta$ to each chosen column. The net effect is to subtract~$\delta$ from all uncovered elements and to add~$\delta$ to all doubly covered elements, while leaving all singly covered elements unchanged. This transformation causes a new zero to appear, while preserving $p$ independent zeroes of the previous matrix (since they were each \vadjust{\goodbreak}% covered only once). If we repeat the Egerv\'ary-K\H{o}nig construction with the same $p$ independent zeroes, we find that either $p$~is no longer maximum or at least one more column has been chosen. (The new zero $r\dash c$ occurs in a row~$r$ that was either unmatched or matched to a previously chosen column, because row~$r$ was not chosen.) Therefore if we repeat the process, we must eventually be able to increase $p$ until finally $p=n$. This will solve the assignment problem, proving the remarkable claim made earlier. @ If the given matrix $A$ has $m$ rows and $n>m$ columns, we can extend it artificially until it is square, by setting $a_{kl}=0$ for all $m\le k<n$ and $0\le l<n$. The construction above will then apply. But we need not waste time making such an extension, because it suffices to run the algorithm on the original $m\times n$ matrix until $m$ independent zeroes have been found. The reason is that the set of matched vertices always grows monotonically in the Egerv\'ary-K\H{o}nig construction: If a column is matched at some stage, it will remain matched from that time on, although it might well change partners. The $n-m$ dummy rows at the bottom of~$A$ are always chosen to be part of the covering; so the dummy entries become nonzero only in the columns that are part of some covering. Such columns are part of some matching, so they are part of the final matching. Therefore at most $m$ columns of the dummy entries become nonzero during the procedure. We can always find $n-m$ independent zeroes in the $n-m$ dummy rows of the matrix, so we need not deal with the dummy elements explicitly. @ It has been convenient to describe the algorithm by saying that we add and subtract constants to and from the columns and rows of~$A$. But all those additions and subtractions can take a lot of time. So we will merely pretend to make the adjustments that the method calls for; we will represent them implicitly by two vectors $(\sigma_0,\ldots,\sigma_{m-1})$ and $(\tau_0,\ldots,\tau_{n-1})$. Then the current value of each matrix entry will be $a_{kl}-\sigma_k+\tau_{\,l}$, instead of $a_{kl}$. The ``zeroes'' will be positions such that $a_{kl}=\sigma_k-\tau_{\,l}$. Initially we will set $\tau_{\,l}=0$ for $0\le l<n$ and $\sigma_k= \min\{a_{k0},\ldots,a_{k(n-1)}\}$ for $0\le k<m$. If $m=n$ we can also make sure that there's a zero in every column by subtracting $\min\{a_{0l},\ldots,a_{(n-1)l}\}$ from $a_{kl}$ for all $k$ and~$l$. (This initial adjustment can conveniently be made to the original matrix entries, instead of indirectly via the $\tau$'s.) Users can discover if such a transformation is worthwhile by trying the program both with and without the \.{-h} option. We have been saying a lot of things and proving a bunch of theorems, without writing any code. Let's get back into programming mode by writing the routine that is called into action when the \.{-h} option has been specified: @d aa(k,l) *(mtx+k*n+l) /* a macro to access the matrix elements */ @<Subtract column minima in order to start with lots of zeroes@>= { for (l=0; l<n; l++) { o,s=aa(0,l); /* the |o| macro counts one mem */ for (k=1;k<n;k++) if (o,aa(k,l)<s) s=aa(k,l); if (s!=0) for (k=0;k<n;k++) oo,aa(k,l)-=s; /* |oo| counts two mems */ } if (verbose) printf(" The heuristic has cost %ld mems.\n",mems); } @ @<Local var...@>= register long k; /* the current row of interest */ register long l; /* the current column of interest */ register long j; /* another interesting column */ register long s; /* the current matrix element of interest */ @* Algorithmic details. The algorithm sketched above is quite simple, except that we did not discuss how to determine the chosen columns~$c^{(q)}$ that are reachable by paths of the stated form $(*)$. It is easy to find all such columns by constructing an unordered forest whose nodes are rows, beginning with all unmatched rows~$r^{(0)}$ and adding a row~$r$ for which $c\ddash r$ when $c$ is adjacent to a row already in the forest. Our data structure, which is based on suggestions of Papadimitriou and @^Papadimitriou, Christos Harilaos@> @^Steiglitz, Kenneth@> Steiglitz [{\sl Combinatorial Optimization\/} (Prentice-Hall, 1982), $\mathchar"278$11.1], will use several arrays. If row~$r$ is matched with column~$c$, we will have |col_mate[r]=c| and |row_mate[c]=r|; if row~$r$ is unmatched, |col_mate[r]| will be |-1|, and if column~$c$ is unmatched, |row_mate[c]| will be |-1|. If column~$c$ has a mate and is also reachable in a path of the form $(*)$, we will have $|parent_row|[c]=r'$ for some $r'$ in the forest. Otherwise column~$c$ is not chosen, and we will have |parent_row[c]=-1|. The rows in the current forest will be called |unchosen_row[0]| through |unchosen_row[t-1]|, where |t| is the current total number of nodes. The amount $\sigma_k$ subtracted from row $k$ is called |row_dec[k]|; the amount $\tau_{\,l}$ added to column~$l$ is called |col_inc[l]|. To compute the minimum uncovered element efficiently, we maintain a quantity called |slack[l]|, which represents the minimum uncovered element in each column. More precisely, if column~$l$ is not chosen, |slack[l]| is the minimum of $a_{kl} -\sigma_k+\tau_{\,l}$ for $k\in\{|unchosen_row|[0],\ldots,\allowbreak |unchosen_row|[q-1]\}$, where $q\le t$ is the number of rows in the forest that we have explored so far. We also remember |slack_row[l]|, the number of a row where the stated minimum occurs. Column $l$ is chosen if and only if |parent_row[l]>=0|. We will arrange things so that we also have |slack[l]=0| in every chosen column. @<Local var...@>= long* col_mate; /* the column matching a given row, or $-1$ */ long* row_mate; /* the row matching a given column, or $-1$ */ long* parent_row; /* ancestor of a given column's mate, or $-1$ */ long* unchosen_row; /* node in the forest */ long t; /* total number of nodes in the forest */ long q; /* total number of explored nodes in the forest */ long* row_dec; /* $\sigma_k$, the amount subtracted from a given row */ long* col_inc; /* $\tau_{\,l}$, the amount added to a given column */ long* slack; /* minimum uncovered entry seen in a given column */ long* slack_row; /* where the |slack| in a given column can be found */ long unmatched; /* this many rows have yet to be matched */ @ @<Allocate the intermediate data structures@>= col_mate=gb_typed_alloc(m,long,working_storage); row_mate=gb_typed_alloc(n,long,working_storage); parent_row=gb_typed_alloc(n,long,working_storage); unchosen_row=gb_typed_alloc(m,long,working_storage); row_dec=gb_typed_alloc(m,long,working_storage); col_inc=gb_typed_alloc(n,long,working_storage); slack=gb_typed_alloc(n,long,working_storage); slack_row=gb_typed_alloc(n,long,working_storage); if (gb_trouble_code) { fprintf(stderr,"Sorry, out of memory!\n"); return -3; } @ The algorithm operates in stages, where each stage terminates when we are able to increase the number of matched elements. The first stage is different from the others; it simply goes through the matrix and looks for zeroes, matching as many rows and columns as it can. This stage also initializes table entries that will be useful in later stages. @d INF 0x7fffffff /* infinity (or darn near) */ @<Do the initial stage@>= t=0; /* the forest starts out empty */ for (l=0; l<n; l++) { o,row_mate[l]=-1; o,parent_row[l]=-1; o,col_inc[l]=0; o,slack[l]=INF; } for (k=0; k<m; k++) { o,s=aa(k,0); /* get ready to calculate the minimum entry of row $k$ */ for (l=1; l<n; l++) if (o,aa(k,l)<s) s=aa(k,l); o,row_dec[k]=s; for (l=0; l<n; l++) if ((o,s==aa(k,l)) && (o,row_mate[l]<0)) { o,col_mate[k]=l; o,row_mate[l]=k; if (verbose>1) printf(" matching col %ld==row %ld\n",l,k); goto row_done; } o,col_mate[k]=-1; if (verbose>1) printf(" node %ld: unmatched row %ld\n",t,k); o,unchosen_row[t++]=k; row_done:; } @ If a subsequent stage has not succeeded in matching every row, we prepare for a new stage by reinitializing the forest as follows. @<Get ready for another stage@>= t=0; for (l=0; l<n; l++) { o,parent_row[l]=-1; o,slack[l]=INF; } for (k=0; k<m; k++) if (o,col_mate[k]<0) { if (verbose>1) printf(" node %ld: unmatched row %ld\n",t,k); o,unchosen_row[t++]=k; } @ Here, then, is the algorithm's overall control structure. There are at most $m$ stages, and each stage does $O(mn)$ operations, so the total running time is $O(m^2n)$. @<Do the Hungarian algorithm@>= @<Do the initial stage@>; if (t==0) goto done; unmatched=t; while(1) { if (verbose) printf(" After %ld mems I've matched %ld rows.\n",mems,m-t); q=0; while(1) { while (q<t) { @<Explore node |q| of the forest; if the matching can be increased, |goto breakthru|@>; q++; } @<Introduce a new zero into the matrix by modifying |row_dec| and |col_inc|; if the matching can be increased, |goto breakthru|@>; } breakthru: @<Update the matching by pairing row $k$ with column $l$@>; if(--unmatched==0) goto done; @<Get ready for another stage@>; } done: @<Doublecheck the solution@>; @ @<Explore node |q| of the forest; if the matching can be increased, |goto breakthru|@>= { o,k=unchosen_row[q]; o,s=row_dec[k]; for (l=0; l<n; l++) if (o,slack[l]) {@+register long del; oo,del=aa(k,l)-s+col_inc[l]; if (del<slack[l]) { if (del==0) { /* we found a new zero */ if (o,row_mate[l]<0) goto breakthru; o,slack[l]=0; /* this column will now be chosen */ o,parent_row[l]=k; if (verbose>1) printf(" node %ld: row %ld==col %ld--row %ld\n", t,row_mate[l],l,k); oo,unchosen_row[t++]=row_mate[l]; }@+else { o,slack[l]=del; o,slack_row[l]=k; } } } } @ At this point, column $l$ is unmatched, and row $k$ is in the forest. By following parent links in the forest, we can rematch rows and columns so that a previously unmatched row~$r^{(0)}$ gets a mate. @<Update the matching by pairing row $k$ with column $l$@>= if (verbose) printf(" Breakthrough at node %ld of %ld!\n",q,t); while (1) { o,j=col_mate[k]; o,col_mate[k]=l; o,row_mate[l]=k; if (verbose>1) printf(" rematching col %ld==row %ld\n",l,k); if (j<0) break; o,k=parent_row[j]; l=j; } @ If we get to this point, we have explored the entire forest; none of the unchosen rows has led to a breakthrough. An unchosen column with smallest |slack| will allow us to make further progress. @<Introduce a new zero into the matrix by modifying |row_dec| and |col_inc|; if the matching can be increased, |goto breakthru|@>= s=INF; for (l=0; l<n; l++) if (o,slack[l] && slack[l]<s) s=slack[l]; for (q=0; q<t; q++) ooo,row_dec[unchosen_row[q]]+=s; for (l=0; l<n; l++) if (o,slack[l]) { /* column $l$ is not chosen */ o,slack[l]-=s; if (slack[l]==0) @<Look at a new zero, and |goto breakthru| with |col_inc| up to date if there's a breakthrough@>; }@+else oo,col_inc[l]+=s; @ There might be several columns tied for smallest slack. If any of them leads to a breakthrough, we are very happy; but we must finish the loop on~|l| before going to |breakthru|, because the |col_inc| variables need to be maintained for the next stage. Within column |l|, there might be several rows that produce the same slack; we have remembered only one of them, |slack_row[l]|. Fortunately, one is sufficient for our purposes. Either we have a breakthrough or we choose column~|l|, regardless of which row or rows led us to consider that column. @<Look at a new zero, and |goto breakthru| with |col_inc| up to date if there's a breakthrough@>= { o,k=slack_row[l]; if (verbose>1) printf(" Decreasing uncovered elements by %ld produces zero at [%ld,%ld]\n", s,k,l); if (o,row_mate[l]<0) { for (j=l+1; j<n; j++) if (o,slack[j]==0) oo,col_inc[j]+=s; goto breakthru; }@+else { /* not a breakthrough, but the forest continues to grow */ o,parent_row[l]=k; if (verbose>1) printf(" node %ld: row %ld==col %ld--row %ld\n", t,row_mate[l],l,k); oo,unchosen_row[t++]=row_mate[l]; } } @ The code in the present section is redundant, unless cosmic radiation has caused the hardware to malfunction. But there is some reassurance whenever we find that mathematics still appears to be consistent, so the author could not resist writing these few unnecessary lines, which verify that the assignment problem has indeed been solved optimally. (We don't count the mems.) @^discussion of \\{mems}@> @<Doublecheck...@>= for (k=0;k<m;k++) for (l=0;l<n;l++) if (aa(k,l)<row_dec[k]-col_inc[l]) { fprintf(stderr,"Oops, I made a mistake!\n"); return -6; /* can't happen */ } for (k=0;k<m;k++) { l=col_mate[k]; if (l<0 || aa(k,l)!=row_dec[k]-col_inc[l]) { fprintf(stderr,"Oops, I blew it!\n"); return-66; /* can't happen */ } } k=0; for (l=0;l<n;l++) if (col_inc[l]) k++; if (k>m) { fprintf(stderr,"Oops, I adjusted too many columns!\n"); return-666; /* can't happen */ } @* Interfacing. A few nitty-gritty details still need to be handled: Our algorithm is not symmetric between rows and columns, and it works only for $m\le n$; so we will transpose the matrix when $m>n$. Furthermore, our algorithm minimizes, but we actually want it to maximize (except when |compl| is nonzero). Hence, we want to make the following transformations to the data before processing it with the algorithm developed above. @<Solve the assignment problem@>= if (m>n) @<Transpose the matrix@>@; else transposed=0; @<Allocate the intermediate data structures@>; if (compl==0) for (k=0; k<m; k++) for (l=0; l<n; l++) aa(k,l)=d-aa(k,l); if (heur) @<Subtract column minima...@>; @<Do the Hungarian algorithm@>; @ @<Transpose...@>= { if (verbose>1) printf("Temporarily transposing rows and columns...\n"); tmtx=gb_typed_alloc(m*n,long,working_storage); if (tmtx==NULL) { fprintf(stderr,"Sorry, out of memory!\n");@+return -4; } for (k=0; k<m; k++) for (l=0; l<n; l++) *(tmtx+l*m+k)=*(mtx+k*n+l); m=n;@+n=k; /* |k| holds the former value of |m| */ mtx=tmtx; transposed=1; } @ @<Local v...@>= long* tmtx; /* the transpose of |mtx| */ long transposed; /* has the data been transposed? */ @ @<Display the solution@>= { printf("The following entries produce an optimum assignment:\n"); for (k=0; k<m; k++) printf(" [%ld,%ld]\n",@| transposed? col_mate[k]:k,@| transposed? k:col_mate[k]); } @* Encapsulated PostScript. A special output file called \.{lisa.eps} is written if the user has selected the \.{-P} option. This file contains a sequence of PostScript commands that can be used to generate an illustration within many kinds of documents. For example, if \TEX/ is being used with the \.{dvips} output driver from Radical Eye Software and with the @.dvips@> associated \.{epsf.tex} macros, one can say $$\.{\\epsfxsize=10cm \\epsfbox\{lisa.eps\}}$$ within a \TEX/ document and the illustration will be typeset in a box that is 10 centimeters wide. The conventions of PostScript allow the illustration to be scaled to any size. Best results are probably obtained if each pixel is at least one millimeter wide (about 1/25 inch) when printed. The illustration is formed by first ``painting'' the input data as a rectangle of pixels, with up to 256 shades of gray. Then the solution pixels are framed in black, with a white trim just inside the black edges to help make the frame visible in already-dark places. The frames are created by painting over the original image; the center of each solution pixel retains its original color. Encapsulated PostScript files have a simple format that is recognized by many software packages and printing devices. We use a subset of PostScript that should be easy to convert to other languages if necessary. @<Output the input matrix in PostScript format@>= { eps_file=fopen("lisa.eps","w"); if (!eps_file) { fprintf(stderr,"Sorry, I can't open the file `lisa.eps'!\n"); PostScript=0; }@+else { fprintf(eps_file,"%%!PS-Adobe-3.0 EPSF-3.0\n"); /* \.{1.0} and \.{2.0} also OK */ fprintf(eps_file,"%%%%BoundingBox: -1 -1 %ld %ld\n",n+1,m+1); fprintf(eps_file,"/buffer %ld string def\n",n); fprintf(eps_file,"%ld %ld 8 [%ld 0 0 -%ld 0 %ld]\n",n,m,n,m,m); fprintf(eps_file,"{currentfile buffer readhexstring pop} bind\n"); fprintf(eps_file,"gsave %ld %ld scale image\n",n,m); for (k=0;k<m;k++) @<Output row |k| as a hexadecimal string@>; fprintf(eps_file,"grestore\n"); } } @ @<Glob...@>= FILE *eps_file; /* file for encapsulated PostScript output */ @ This program need not produce machine-independent output, so we can safely use floating-point arithmetic here. At most 64 characters (32 pixel-bytes) are output on each line. @<Output row |k|...@>= {@+register float conv=255.0/(float)d; register long x; for (l=0; l<n; l++) { x=(long)(conv*(float)(compl?d-aa(k,l):aa(k,l))); fprintf(eps_file,"%02lx",x>255?255L:x); if ((l&0x1f)==0x1f) fprintf(eps_file,"\n"); } if (n&0x1f) fprintf(eps_file,"\n"); } @ @<Output the solution in PostScript format@>= { fprintf(eps_file, "/bx {moveto 0 1 rlineto 1 0 rlineto 0 -1 rlineto closepath\n"); fprintf(eps_file," gsave .3 setlinewidth 1 setgray clip stroke"); fprintf(eps_file," grestore stroke} bind def\n"); fprintf(eps_file," .1 setlinewidth\n"); for (k=0; k<m; k++) fprintf(eps_file," %ld %ld bx\n",@| transposed? k:col_mate[k],@| transposed? n-1-col_mate[k]:m-1-k); fclose(eps_file); } @* Index. As usual, we close with a list of identifier definitions and uses. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������book_components.w�����������������������������������������������������������������������������������0000444�0001750�0001750�00000055127�05526102624�012601� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{BOOK\_\kern.05emCOMPONENTS} \def\<#1>{$\langle${\rm#1}$\rangle$} \prerequisite{GB\_\,BOOKS} @* Bicomponents. This demonstration program computes the biconnected components of GraphBase graphs derived from world literature, using a variant of Hopcroft and Tarjan's algorithm [R. E. Tarjan, ``Depth-first @^Hopcroft, John Edward@> @^Tarjan, Robert Endre@> search and linear graph algorithms,'' {\sl SIAM Journal on Computing\/ \bf1} (1972), 146--160]. Articulation points and ordinary (connected) components are also obtained as byproducts of the computation. Two edges belong to the same biconnected component---or ``bicomponent'' for short---if and only if they are identical or both belong to a simple cycle. This defines an equivalence relation on edges. The bicomponents of a connected graph form a free tree, if we say that two bicomponents are adjacent when they have a common vertex (i.e., when there is a vertex belonging to at least one edge in each of the bicomponents). Such a vertex is called an {\sl articulation point\/}; there is a unique articulation point between any two adjacent bicomponents. If we choose one bicomponent to be the ``root'' of the free tree, the other bicomponents can be represented conveniently as lists of vertices, with the articulation point that leads toward the root listed last. This program displays the bicomponents in exactly that way. @ We permit command-line options in typical \UNIX/ style so that a variety of graphs can be studied: The user can say `\.{-t}\<title>', `\.{-n}\<number>', `\.{-x}\<number>', `\.{-f}\<number>', `\.{-l}\<number>', `\.{-i}\<number>', `\.{-o}\<number>', and/or `\.{-s}\<number>' to change the default values of the parameters in the graph generated by |book(t,n,x,f,l,i,o,s)|. When the bicomponents are listed, each character in the book is identified by a two-letter code, as found in the associated data file. An explanation of these codes will appear first if the \.{-v} or \.{-V} option is specified. The \.{-V} option prints a fuller explanation than~\.{-v}; it also shows each character's weighted number of appearances. The special command-line option \.{-g}$\langle\,$filename$\,\rangle$ overrides all others. It substitutes an external graph previously saved by |save_graph| for the graphs produced by |book|. @^UNIX dependencies@> @p #include "gb_graph.h" /* the GraphBase data structures */ #include "gb_books.h" /* the |book| routine */ #include "gb_io.h" /* the |imap_chr| routine */ #include "gb_save.h" /* |restore_graph| */ @h@# @<Global variables@>@; @<Subroutines@>@; main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ {@+Graph *g; /* the graph we will work on */ register Vertex *v; /* the current vertex of interest */ char *t="anna"; /* the book to use */ unsigned long n=0; /* the desired number of vertices (0 means infinity) */ unsigned long x=0; /* the number of major characters to exclude */ unsigned long f=0; /* the first chapter to include */ unsigned long l=0; /* the last chapter to include (0 means infinity) */ long i=1; /* the weight for appearances in selected chapters */ long o=1; /* the weight for appearances in unselected chapters */ long s=0; /* the random number seed */ @<Scan the command-line options@>; if (filename) g=restore_graph(filename); else g=book(t,n,x,f,l,i,o,s); if (g==NULL) { fprintf(stderr,"Sorry, can't create the graph! (error code %ld)\n", panic_code); return -1; } printf("Biconnectivity analysis of %s\n\n",g->id); if (verbose) @<Print the cast of selected characters@>; @<Perform the Hopcroft-Tarjan algorithm on |g|@>; return 0; /* normal exit */ } @ @<Scan the command-line options@>= while (--argc) { @^UNIX dependencies@> if (strncmp(argv[argc],"-t",2)==0) t=argv[argc]+2; else if (sscanf(argv[argc],"-n%lu",&n)==1) ; else if (sscanf(argv[argc],"-x%lu",&x)==1) ; else if (sscanf(argv[argc],"-f%lu",&f)==1) ; else if (sscanf(argv[argc],"-l%lu",&l)==1) ; else if (sscanf(argv[argc],"-i%ld",&i)==1) ; else if (sscanf(argv[argc],"-o%ld",&o)==1) ; else if (sscanf(argv[argc],"-s%ld",&s)==1) ; else if (strcmp(argv[argc],"-v")==0) verbose=1; else if (strcmp(argv[argc],"-V")==0) verbose=2; else if (strncmp(argv[argc],"-g",2)==0) filename=argv[argc]+2; else { fprintf(stderr, "Usage: %s [-ttitle][-nN][-xN][-fN][-lN][-iN][-oN][-sN][-v][-gfoo]\n", argv[0]); return -2; } } if (filename) verbose=0; @ @<Subroutines@>= char *filename=NULL; /* external graph to be restored */ char code_name[3][3]; char *vertex_name(v,i) /* return (as a string) the name of vertex |v| */ Vertex *v; char i; /* |i| should be 0, 1, or 2 to avoid clash in |code_name| array */ { if (filename) return v->name; /* not a |book| graph */ code_name[i][0]=imap_chr(v->short_code/36); code_name[i][1]=imap_chr(v->short_code%36); return code_name[i]; } @ @<Print the cast of selected characters@>= { for (v=g->vertices;v<g->vertices+g->n;v++) { if (verbose==1) printf("%s=%s\n",vertex_name(v,0),v->name); else printf("%s=%s, %s [weight %ld]\n",vertex_name(v,0),v->name,v->desc,@| i*v->in_count+o*v->out_count); } printf("\n"); } @*The algorithm. The Hopcroft-Tarjan algorithm is inherently recursive. We will implement the recursion explicitly via linked lists, instead of using \CEE/'s runtime stack, because some computer systems bog down in the presence of deeply nested recursion. Each vertex goes through three stages during the algorithm. First it is ``unseen''; then it is ``active''; finally it becomes ``settled,'' when it has been assigned to a bicomponent. The data structures that represent the current state of the algorithm are implemented by using five of the utility fields in each vertex: |rank|, |parent|, |untagged|, |link|, and |min|. We will consider each of these in turn. @ First is the integer |rank| field, which is zero when a vertex is unseen. As soon as the vertex is first examined, it becomes active and its |rank| becomes and remains nonzero. Indeed, the $k$th vertex to become active will receive rank~$k$. It's convenient to think of the Hopcroft-Tarjan algorithm as a simple adventure game in which we want to explore all the rooms of a cave. Passageways between the rooms allow two-way travel. When we come into a room for the first time, we assign a new number to that room; this is its rank. Later on we might happen to come into the same room again, and we will notice that it has nonzero rank. Then we'll be able to make a quick exit, saying ``we've already been here.'' (The extra complexities of computer games, like dragons that might need to be vanquished, do not arise.) @d rank z.I /* the |rank| of a vertex is stored in utility field |z| */ @<Glob...@>= long nn; /* the number of vertices that have been seen */ @ The active vertices will always form an oriented tree, whose arcs are a subset of the arcs in the original graph. A tree arc from |u| to~|v| will be represented by |v->parent==u|. Every active vertex has a parent, which is usually another active vertex; the only exception is the root of the tree, whose |parent| is a dummy vertex called |dummy|. The dummy vertex has rank zero. In the cave analogy, the ``parent'' of room |v| is the room we were in immediately before entering |v| the first time. By following parent pointers, we will be able to leave the cave whenever we want. @d parent y.V /* the |parent| of a vertex is stored in utility field |y| */ @<Glob...@>= Vertex dummy; /* imaginary parent of the root vertex */ @ All edges in the original undirected graph are explored systematically during a depth-first search. Whenever we look at an edge, we tag it so that we won't need to explore it again. In a cave, for example, we might mark each passageway between rooms once we've tried to go through~it. In a GraphBase graph, undirected edges are represented as a pair of directed arcs. Each of these arcs will be examined and eventually tagged. The algorithm doesn't actually place a tag on its |Arc| records; instead, each vertex |v| has a pointer |v->untagged| that leads to all hitherto-unexplored arcs from~|v|. The arcs of the list that appear between |v->arcs| and |v->untagged| are the ones already examined. @d untagged x.A /* the |untagged| field points to an |Arc| record, or |NULL| */ @ The algorithm maintains a special stack, the |active_stack|, which contains all the currently active vertices. Each vertex has a |link| field that points to the vertex that is next lower on its stack, or to |NULL| if the vertex is at the bottom. The vertices on |active_stack| always appear in increasing order of rank from bottom to top. @d link w.V /* the |link| field of a vertex occupies utility field |w| */ @<Glob...@>= Vertex * active_stack; /* the top of the stack of active vertices */ @ Finally there's a |min| field, which is the tricky part that makes everything work. If vertex~|v| is unseen or settled, its |min| field is irrelevant. Otherwise |v->min| points to the active vertex~|u| of smallest rank having the following property: There is a directed path from |v| to |u| consisting of zero or more mature tree arcs followed by a single non-tree arc. What is a tree arc, you ask. And what is a mature arc? Good questions. At the moment when arcs of the graph are tagged, we classify them either as tree arcs (if they correspond to a new |parent| link in the tree of active nodes) or non-tree arcs (otherwise). The tree arcs therefore correspond to passageways that have led us to new territory. A tree arc becomes mature when it is no longer on the path from the root to the current vertex being explored. We also say that a vertex becomes mature when it is no longer on that path. All arcs from a mature vertex have been tagged. We said before that every vertex is initially unseen, then active, and finally settled. With our new definitions, we see further that every arc starts out untagged, then it becomes either a non-tree arc or a tree arc. In the latter case, the arc begins as an immature tree arc and eventually matures. The dummy vertex is considered to be active, and we assume that there is a non-tree arc from the root vertex back to |dummy|. Thus there is a non-tree arc from |v| to |v->parent| for all~|v|, and |v->min| will always point to a vertex whose rank is less than or equal to |v->parent->rank|. It will turn out that |v->min| is always an ancestor of~|v|. Just believe these definitions, for now. All will become clear soon. @d min v.V /* the |min| field of a vertex occupies utility field |v| */ @ Depth-first search explores a graph by systematically visiting all vertices and seeing what they can lead to. In the Hopcroft-Tarjan algorithm, as we have said, the active vertices form an oriented tree. One of these vertices is called the current vertex. If the current vertex still has an arc that hasn't been tagged, we tag one such arc and there are two cases: Either the arc leads to an unseen vertex, or it doesn't. If it does, the arc becomes a tree arc; the previously unseen vertex becomes active, and it becomes the new current vertex. On the other hand if the arc leads to a vertex that has already been seen, the arc becomes a non-tree arc and the current vertex doesn't change. Finally there will come a time when the current vertex~|v| has no untagged arcs. At this point, the algorithm might decide that |v| and all its descendants form a bicomponent, together with |v->parent|. Indeed, this condition turns out to be true if and only if |v->min==v->parent|; a proof appears below. If so, |v| and all its descendants become settled, and they leave the tree. If not, the tree arc from |v|'s parent~|u| to~|v| becomes mature, so the value of |v->min| is used to update the value of |u->min|. In both cases, |v| becomes mature and the new current vertex will be the parent of~|v|. Notice that only the value of |u->min| needs to be updated, when the arc from |u| to~|v| matures; all other values |w->min| stay the same, because a newly mature arc has no mature predecessors. The cave analogy helps to clarify the situation: Suppose we enter room~|v| from room~|u|. If there's no way out of the subcave starting at~|v| unless we come back through~|u|, and if we can get to~|u| from all of |v|'s descendants without passing through~|v|, then room~|v| and its descendants will become a bicomponent together with~|u|. Once such a bicomponent is identified, we close it off and don't explore that subcave any further. If |v| is the root of the tree, it always has |v->min==dummy|, so it will always define a new bicomponent at the moment it matures. Then the depth-first search will terminate, since |v|~has no real parent. But the Hopcroft-Tarjan algorithm will press on, trying to find a vertex~|u| that is still unseen. If such a vertex exists, a new depth-first search will begin with |u| as the root. This process keeps on going until at last all vertices are happily settled. The beauty of this algorithm is that it all works very efficiently when we organize it as follows: @<Perform the Hopcroft-Tarjan algorithm on |g|@>= @<Make all vertices unseen and all arcs untagged@>; for (vv=g->vertices; vv<g->vertices+g->n; vv++) if (vv->rank==0) /* |vv| is still unseen */ @<Perform a depth-first search with |vv| as the root, finding the bicomponents of all unseen vertices reachable from~|vv|@>; @ @<Glob...@>= Vertex *vv; /* sweeps over all vertices, making sure none is left unseen */ @ It's easy to get the data structures started, according to the conventions stipulated above. @<Make all vertices unseen...@>= for (v=g->vertices; v<g->vertices+g->n; v++) { v->rank=0; v->untagged=v->arcs; } nn=0; active_stack=NULL; dummy.rank=0; @ The task of starting a depth-first search isn't too bad either. Throughout this part of the algorithm, variable~|v| will point to the current vertex. @<Perform a depth-first search with |vv| as the root...@>= { v=vv; v->parent=&dummy; @<Make vertex |v| active@>; do @<Explore one step from the current vertex~|v|, possibly moving to another current vertex and calling~it~|v|@>@; while (v!=&dummy); } @ @<Make vertex |v| active@>= v->rank=++nn; v->link=active_stack; active_stack=v; v->min=v->parent; @ Now things get interesting. But we're just doing what any well-organized spelunker would do when calmly exploring a cave. There are three main cases, depending on whether the current vertex stays where it is, moves to a new child, or backtracks to a parent. @<Explore one step from the current vertex~|v|, possibly moving to another current vertex and calling~it~|v|@>= {@+register Vertex *u; /* a vertex adjacent to |v| */ register Arc *a=v->untagged; /* |v|'s first remaining untagged arc, if any */ if (a) { u=a->tip; v->untagged = a->next; /* tag the arc from |v| to |u| */ if (u->rank) { /* we've seen |u| already */ if (u->rank < v->min->rank) v->min=u; /* non-tree arc, just update |v->min| */ }@+else { /* |u| is presently unseen */ u->parent = v; /* the arc from |v| to |u| is a new tree arc */ v = u; /* |u| will now be the current vertex */ @<Make vertex |v| active@>; } }@+else { /* all arcs from |v| are tagged, so |v| matures */ u=v->parent; /* prepare to backtrack in the tree */ if (v->min==u) @<Remove |v| and all its successors on the active stack from the tree, and report them as a bicomponent of the graph together with~|u|@>@; else /* the arc from |u| to |v| has just matured, making |v->min| visible from |u| */@, if (v->min->rank < u->min->rank) u->min=v->min; v=u; /* the former parent of |v| is the new current vertex |v| */ } } @ The elements of the active stack are always in order by rank, and all children of a vertex~|v| in the tree have rank higher than~|v|. The Hopcroft-Tarjan algorithm relies on a converse property: {\sl All active nodes whose rank exceeds that of the current vertex~|v| are descendants of~|v|.} (This property holds because the algorithm has constructed the tree by assigning ranks in preorder, ``the order of succession to the throne.'' First come |v|'s firstborn and descendants, then the nextborn, and so on.) Therefore the descendants of the current vertex always appear consecutively at the top of the stack. Suppose |v| is a mature, active vertex with |v->min==v->parent|, and let |u=v->parent|. We want to prove that |v| and its descendants, together with~|u| and all edges between these vertices, form a biconnected graph. Call this subgraph~$H$. The parent links define a subtree of~$H$, rooted at~|u|, and |v| is the only vertex having |u| as a parent (because all other vertices are descendants of~|v|). Let |x| be any vertex of~$H$ different from |u| and |v|. Then there is a path from |x| to |x->min| that does not touch~|x->parent|, and |x->min| is a proper ancestor of |x->parent|. This property is sufficient to establish the biconnectedness of~$H$. (A proof appears at the conclusion of this program.) Moreover, we cannot add any more vertices to~$H$ without losing biconnectivity. If |w|~is another vertex, either |w| has been output already as a non-articulation point of a previous biconnected component, or we can prove that there is no path from |w| to~|v| that avoids the vertex~|u|. @ Therefore we are justified in settling |v| and its active descendants now. Removing them from the tree of active vertices does not remove any vertex from which there is a path to a vertex of rank less than |u->rank|. Hence their removal does not affect the validity of the |w->min| value for any vertex~|w| that remains active. A slight technicality arises with respect to whether or not the parent of~|v|, vertex~|u|, is part of the present bicomponent. When |u| is the dummy vertex, we have already printed the final bicomponent of a connected component of the original graph, unless |v| was an isolated vertex. Otherwise |u| is an articulation point that will occur in subsequent bicomponents, unless the new bicomponent is the final bicomponent of a connected component. (This aspect of the algorithm is probably its most subtle point; consideration of an example or two should clarify everything.) We print out enough information for a reader to verify the biconnectedness of the claimed component easily. @<Remove |v| and all its successors on the active stack...@>= if (u==&dummy) { /* |active_stack| contains just |v| */ if (artic_pt) printf(" and %s (this ends a connected component of the graph)\n", vertex_name(artic_pt,0)); else printf("Isolated vertex %s\n",vertex_name(v,0)); active_stack=artic_pt=NULL; }@+else {@+register Vertex *t; /* runs through the vertices of the new bicomponent */ if (artic_pt) printf(" and articulation point %s\n",vertex_name(artic_pt,0)); t=active_stack; active_stack=v->link; printf("Bicomponent %s", vertex_name(v,0)); if (t==v) putchar('\n'); /* single vertex */ else { printf(" also includes:\n"); while (t!=v) { printf(" %s (from %s; ..to %s)\n", vertex_name(t,0), vertex_name(t->parent,1),vertex_name(t->min,2)); t=t->link; } } artic_pt=u; /* the printout will be finished later */ } @ Like all global variables, |artic_pt| is initially zero (|NULL|). @<Glob...@>= Vertex *artic_pt; /* articulation point to be printed if the current bicomponent isn't the last in its connected component */ @*Proofs. The program is done, but we still should prove that it works. First we want to clarify the informal definition by verifying that the cycle relation between edges, as stated in the introduction, is indeed an equivalence relation. \def\dash{\mathrel-\joinrel\joinrel\mathrel-} Suppose $u\dash v$ and $w\dash x$ are edges of a simple cycle~$C$, while $w\dash x$ and $y\dash z$ are edges of a simple cycle~$D$. We want to show that there is a simple cycle containing the edges $u\dash v$ and $y\dash z$. There are vertices $a,b\in C$ such that $a\dash^\ast y\dash z\dash^\ast b$ is a subpath of~$D$ containing no other vertices of $C$ besides $a$ and~$b$. Join this subpath to the subpath in $C$ that runs from $b$ to~$a$ through the edge $u\dash v$. Therefore the stated relation between edges is transitive, and it is an equivalence relation. A graph is biconnected if it contains a single vertex, or if each of its vertices is adjacent to at least one other vertex and any two edges are equivalent. @ Next we prove the well-known fact that a graph is biconnected if and only if it is connected and, for any three distinct vertices $x$, $y$,~$z$, it contains a path from $x$ to~$y$ that does not touch~$z$. Call the latter condition property~P. Suppose $G$ is biconnected, and let $x,y$ be distinct vertices of~$G$. Then there exist edges $u\dash x$ and $v\dash y$, which are either identical (hence $x$ and~$y$ are adjacent) or part of a simple cycle (hence there are two paths from $x$ to~$y$, having no other vertices in common). Thus $G$ has property~P. Suppose, conversely, that $G$ has property~P, and let $u\dash v, w\dash x$ be distinct edges of~$G$. We want to show that these edges belong to some simple cycle. The proof is by induction on $k=\min\bigl(d(u,w),d(u,x),\allowbreak d(v,w),d(v,x)\bigr)$, where $d$~denotes distance. If $k=0$, property~P gives the result directly. If $k>0$, we can assume by symmetry that $k=d(u,w)$; so there's a vertex $y$ with $u\dash y$ and $d(y,w)=k-1$. And we have $u\dash v$ equivalent to $u\dash y$ by property~P, $u\dash y$ equivalent to $w\dash x$ by induction, hence $u\dash v$ is equivalent to $w\dash x$ by transitivity. @ Finally, we prove that $G$ satisfies property~P if it has the following properties: (1)~There are two distinguished vertices $u$ and~$v$. (2)~Some of the edges of~$G$ form a subtree rooted at~$u$, and $v$ is the only vertex whose parent in this tree is~$u$. (3)~Every vertex~$x$ other than $u$ or $v$ has a path to its grandparent that does not go through its parent. If property P doesn't hold, there are distinct vertices $x,y,z$ such that every path from $x$ to~$y$ goes through~$z$. In particular, $z$ must be between $x$ and~$y$ in the unique path~$\pi$ that joins them in the subtree. It follows that $z\ne u$ is the parent of some node $z'$ in that path; hence $z'\ne u$ and $z'\ne v$. But we can avoid $z$ by going from $z'$ to the grandparent of $z'$, which is also part of path~$\pi$ unless $z$ is also the parent of another node $z''$ in~$\pi$. In the latter case, however, we can avoid $z$ by going from $z'$ to the grandparent of $z'$ and from there to $z''$, since $z'$ and $z''$ have the same grandparent. @* Index. We close with a list that shows where the identifiers of this program are defined and used. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������econ_order.w����������������������������������������������������������������������������������������0000444�0001750�0001750�00000031221�05437512111�011503� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{ECON\_\,ORDER} \def\<#1>{$\langle${\rm#1}$\rangle$} \prerequisite{GB\_\,ECON} @* Near-triangular ordering. This demonstration program takes a matrix of data constructed by the {\sc GB\_\,ECON} module and permutes the economic sectors so that the first sectors of the ordering tend to be producers of primary materials for other industries, while the last sectors tend to be final-product industries that deliver their output mostly to end users. More precisely, suppose the rows of the matrix represent the outputs of a sector and the columns represent the inputs. This program attempts to find a permutation of rows and columns that minimizes the sum of the elements below the main diagonal. (If this sum were zero, the matrix would be upper triangular; each supplier of a sector would precede it in the ordering, while each customer of that sector would follow it.) The general problem of finding a minimizing permutation is NP-complete; it includes, as a very special case, the {\sc FEEDBACK ARC SET} problem discussed in Karp's classic paper [{\sl Complexity of Computer @^Karp, Richard Manning@> Computations} (Plenum Press, 1972), 85--103]. But sophisticated ``branch and cut'' methods have been developed that work well in practice on problems of reasonable size. Here we use a simple heuristic downhill method to find a permutation that is locally optimum, in the sense that the below-diagonal sum does not decrease if any individual sector is moved to another position while preserving the relative order of the other sectors. We start with a random permutation and repeatedly improve it, choosing the improvement that gives the least positive gain at each step. A primary motive for the present implementation was to get further experience with this method of cautious descent, which was proposed by A. M. Gleason in {\sl AMS Proceedings of Symposia in Applied @^Gleason, Andrew Mattei@> Mathematics\/ \bf10} (1958), 175--178. (See the comments following the program below.) @ As explained in {\sc GB\_\,ECON}, the subroutine call |econ(n,2,0,s)| constructs a graph whose |n<=79| vertices represent sectors of the U.S. economy and whose arcs $u\to v$ are assigned numbers corresponding to the flow of products from sector~|u| to sector~|v|. When |n<79|, the |n| sectors are obtained from a basic set of 79 sectors by combining related commodities. If |s=0|, the combination is done in a way that tends to equalize the row sums, while if |s>0|, the combination is done by choosing a random subtree of a given 79-leaf tree; the ``randomness'' is fully determined by the value of~|s|. This program uses two random number seeds, one for |econ| and one for choosing the random initial permutation. The former is called~|s| and the latter is called~|t|. A further parameter, |r|, governs the number of repetitions to be made; the machine will try |r|~different starting permutations on the same matrix. When |r>1|, new solutions are displayed only when they improve on the previous best. By default, |n=79|, |r=1|, and |s=t=0|. The user can change these default parameters by specifying options on the command line, at least in a \UNIX/ implementation, thereby obtaining a variety of special effects. The relevant command-line options are \.{-n}\<number>, \.{-r}\<number>, \.{-s}\<number>, and/or \.{-t}\<number>. Additional options \.{-v} (verbose), \.{-V} (extreme verbosity), and \.{-g} (greedy or steepest descent instead of cautious descent) are also provided. @^UNIX dependencies@> Here is the overall layout of this \CEE/ program: @p #include "gb_graph.h" /* the GraphBase data structures */ #include "gb_flip.h" /* the random number generator */ #include "gb_econ.h" /* the |econ| routine */ @h@# @<Global variables@>@; main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ {@+unsigned long n=79; /* the desired number of sectors */ long s=0; /* random \\{seed} for |econ| */ long t=0; /* random \\{seed} for initial permutation */ unsigned long r=1; /* the number of repetitions */ long greedy=0; /* should we use steepest descent? */ register long j,k; /* all-purpose indices */ @<Scan the command-line options@>; g=econ(n,2L,0L,s); if (g==NULL) { fprintf(stderr,"Sorry, can't create the matrix! (error code %ld)\n", panic_code); return -1; } printf("Ordering the sectors of %s, using seed %ld:\n",g->id,t); printf(" (%s descent method)\n",greedy?"Steepest":"Cautious"); @<Put the graph data into matrix form@>; @<Print an obvious lower bound@>; gb_init_rand(t); while (r--) @<Find a locally optimum permutation and report the below-diagonal sum@>; return 0; /* normal exit */ } @ Besides the matrix $M$ of input/output coefficients, we will find it convenient to use the matrix $\Delta$, where $\Delta_{jk}=M_{jk}-M_{kj}$. @d INF 0x7fffffff /* infinity (or darn near) */ @<Global...@>= Graph *g; /* the graph we will work on */ long mat[79][79]; /* the corresponding matrix */ long del[79][79]; /* skew-symmetric differences */ long best_score=INF; /* the smallest below-diagonal sum we've seen so far */ @ @<Scan the command-line options@>= while (--argc) { @^UNIX dependencies@> if (sscanf(argv[argc],"-n%lu",&n)==1) ; else if (sscanf(argv[argc],"-r%lu",&r)==1) ; else if (sscanf(argv[argc],"-s%ld",&s)==1) ; else if (sscanf(argv[argc],"-t%ld",&t)==1) ; else if (strcmp(argv[argc],"-v")==0) verbose=1; else if (strcmp(argv[argc],"-V")==0) verbose=2; else if (strcmp(argv[argc],"-g")==0) greedy=1; else { fprintf(stderr,"Usage: %s [-nN][-rN][-sN][-tN][-g][-v][-V]\n",argv[0]); return -2; } } @ The optimum permutation is a function only of the $\Delta$ matrix, because we can subtract any constant from both $M_{jk}$ and $M_{kj}$ without changing the basic problem. @<Put the graph data into matrix form@>= {@+register Vertex *v; register Arc *a; n=g->n; for (v=g->vertices;v<g->vertices+n;v++) for (a=v->arcs;a;a=a->next) mat[v-g->vertices][a->tip-g->vertices]=a->flow; for (j=0;j<n;j++) for (k=0;k<n;k++) del[j][k]=mat[j][k]-mat[k][j]; } @ Nontrivial lower bounds that can be made strong enough to find provably optimum solutions to the ordering problem can be based on linear programming, as shown for example by Gr\"otschel, J\"unger, and Reinelt @^Gr\"otschel, Martin@> @^J\"unger, Michael@> @^Reinelt, Gerhard@> [{\sl Operations Research \bf32} (1984), 1195--1220]. The basic idea is to formulate the problem as the task of minimizing $\sum M_{jk}x_{jk}$ for integer variables $x_{jk}\ge0$, subject to the conditions $x_{jk}+x_{kj}=1$ and $x_{ik}\le x_{ij}+x_{jk}$ for all triples $(i,j,k)$ of distinct subscripts; these conditions are necessary and sufficient. Relaxing the integrality constraints gives a lower bound, and we can also add additional inequalities such as $x_{14}+x_{25}+x_{36}+x_{42}+x_{43}+x_{51}+x_{53}+x_{61}+x_{62}\le7$. The interesting story of inequalities like this has been surveyed by P. C. Fishburn [{\sl Mathematical Social Sciences\/ \bf23} (1992), 67--80]. @^Fishburn, Peter Clingerman@> However, our goal is more modest---we just want to study two of the simplest heuristics. So we will be happy with a trivial bound based only on the constraints $x_{jk}+x_{kj}=1$. @<Print an obvious lower bound@>= {@+register long sum=0; for (j=1;j<n;j++) for (k=0;k<j;k++) if (mat[j][k]<=mat[k][j]) sum+=mat[j][k]; else sum+=mat[k][j]; printf("(The amount of feed-forward must be at least %ld.)\n",sum); } @* Descent. At each stage in our search, |mapping| will be the current permutation; in other words, the sector in row and column~|k| will be |g->vertices+mapping[k]|. The current below-diagonal sum will be the value of |score|. We will not actually have to permute anything inside of |mat|. @d sec_name(k) (g->vertices+mapping[k])->name @<Glob...@>= long mapping[79]; /* current permutation */ long score; /* current sum of elements above main diagonal */ long steps; /* the number of iterations so far */ @ @<Find a locally optimum perm...@>= { @<Initialize |mapping| to a random permutation@>; while(1) { @<Figure out the next move to make; |break| if at local optimum@>; if (verbose) printf("%8ld after step %ld\n",score,steps); else if (steps%1000==0 && steps>0) { putchar('.'); fflush(stdout); /* progress report */ } @<Take the next step@>; } printf("\n%s is %ld, found after %ld step%s.\n",@| best_score==INF?"Local minimum feed-forward": "Another local minimum",@| score,steps,steps==1?"":"s"); if (verbose || score<best_score) { printf("The corresponding economic order is:\n"); for (k=0;k<n;k++) printf(" %s\n",sec_name(k)); if (score<best_score) best_score=score; } } @ @<Initialize |mapping| to a random permutation@>= steps=score=0; for (k=0; k<n; k++) { j=gb_unif_rand(k+1); mapping[k]=mapping[j]; mapping[j]=k; } for (j=1; j<n; j++) for (k=0;k<j;k++) score+=mat[mapping[j]][mapping[k]]; if (verbose>1) { printf("\nInitial permutation:\n"); for (k=0;k<n;k++) printf(" %s\n",sec_name(k)); } @ If we move, say, |mapping[5]| to |mapping[3]| and shift the previous entries |mapping[3]| and |mapping[4]| right one, the score decreases by $$\hbox{|del[mapping[5]][mapping[3]]+del[mapping[5]][mapping[4]]|}\,.$$ Similarly, if we move |mapping[5]| to |mapping[7]| and shift the previous entries |mapping[6]| and |mapping[7]| left one, the score decreases by $$\hbox{|del[mapping[6]][mapping[5]]+del[mapping[7]][mapping[5]]|}\,.$$ The number of possible moves is $(n-1)^2$. Our job is to find the one that makes the score decrease, but by as little as possible (or, if |greedy!=0|, to make the score decrease as much as possible). @<Figure out the next move to make; |break| if at local optimum@>= best_d=greedy? 0: INF; best_k=-1; for (k=0;k<n;k++) {@+register long d=0; for (j=k-1;j>=0;j--) { d+=del[mapping[k]][mapping[j]]; @<Record the move from |k| to |j|, if |d| is better than |best_d|@>; } d=0; for (j=k+1;j<n;j++) { d+=del[mapping[j]][mapping[k]]; @<Record the move...@>; } } if (best_k<0) break; @ @<Record the move...@>= if (d>0 && (greedy? d>best_d: d<best_d)) { best_k=k; best_j=j; best_d=d; } @ @<Glob...@>= long best_d; /* best improvement seen so far on this step */ long best_k,best_j; /* moving |best_k| to |best_j| improves by |best_d| */ @ @<Take the next step@>= if (verbose>1) printf("Now move %s to the %s, past\n",sec_name(best_k), best_j<best_k? "left": "right"); j=best_k; k=mapping[j]; do@+{ if (best_j<best_k) mapping[j]=mapping[j-1],j--; else mapping[j]=mapping[j+1],j++; if (verbose>1) printf(" %s (%ld)\n",sec_name(j),@| best_j<best_k?del[mapping[j+1]][k]: del[k][mapping[j-1]]); }@+while(j!=best_j); mapping[j]=k; score-=best_d; steps++; @ How well does cautious descent work? In this application, it is definitely too cautious. For example, after lots of computation with the default settings, it comes up with a pretty good value (457342), but only after taking 39,418 steps! Then (if |r>1|) it tries again and stops with 461584 after 47,634 steps. The greedy algorithm with the same starting permutations obtains the local minimum 457408 after only 93 steps, then 460411 after 83 steps. The greedy algorithm tends to find solutions that are a bit inferior, but it is so much faster that it allows us to run many more experiments. After 20 trials with the default settings, it finds a permutation with only 456315 below the diagonal, and after about 250 more it reduces this upper bound to 456295. (Gerhard Reinelt has proved, via branch-and-cut, that 456295 is in fact optimum.) @^Reinelt, Gerhard@> The method of stratified greed, which is illustrated in the {\sc FOOTBALL} module, should do better than the ordinary greedy algorithm; and interesting results can be expected when stratified greed is compared also to other methods like simulated annealing and genetic breeding. Comparisons should be made by seeing which method can come up with the best upper bound after calculating for a given number of mems (see {\sc MILES\_\,SPAN}). The upper bound obtained in any run is a random variable, so several independent trials of each method should be~made. Question: Suppose we divide the vertices into two subsets and prescribe a fixed permutation on each subset. Is it NP-complete to find the optimum way to merge these two permutations---i.e., to find a permutation, extending the given ones, that has the smallest below-diagonal sum? @* Index. We close with a list that shows where the identifiers of this program are defined and used. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������football.w������������������������������������������������������������������������������������������0000444�0001750�0001750�00000063145�05437512531�011206� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{FOOTBALL} \prerequisite{GB\_\,GAMES} @* Introduction. This demonstration program uses graphs constructed by the {\sc GB\_\,GAMES} module to produce an interactive program called \.{football}, which finds preposterously long chains of scores to ``prove'' that one given team might outrank another by a huge margin. \def\<#1>{$\langle${\rm#1}$\rangle$} The program prompts you for a starting team. If you simply type \<return>, it exits; otherwise you should enter a team name (e.g., `\.{Stanford}') before typing \<return>. Then the program prompts you for another team. If you simply type \<return> at this point, it will go back and ask for a new starting team; otherwise you should specify another name (e.g., `\.{Harvard}'). Then the program finds and displays a chain from the starting team to the other one. For example, you might see the following: $$\vbox{\halign{\tt#\hfil\cr Oct 06: Stanford Cardinal 36, Notre Dame Fighting Irish 31 (+5)\cr Oct 20: Notre Dame Fighting Irish 29, Miami Hurricanes 20 (+14)\cr Jan 01: Miami Hurricanes 46, Texas Longhorns 3 (+57)\cr Nov 03: Texas Longhorns 41, Texas Tech Red Raiders 22 (+76)\cr Nov 17: Texas Tech Red Raiders 62, Southern Methodist Mustangs 7 (+131)\cr Sep 08: Southern Methodist Mustangs 44, Vanderbilt Commodores 7 (+168)\cr \omit\qquad\vdots\cr Nov 10: Cornell Big Red 41, Columbia Lions 0 (+2188)\cr Sep 15: Columbia Lions 6, Harvard Crimson 9 (+2185)\cr}}$$ The chain isn't necessarily optimal; it's just this particular program's best guess. Another chain, which establishes a victory margin of $+2279$ points, can in fact be produced by modifying this program to search back from Harvard instead of forward from Stanford. Algorithms that find even better chains should be fun to invent. Actually this program has two variants. If you invoke it by saying simply `\.{football}', you get chains found by a simple ``greedy algorithm.'' But if you invoke it by saying `\.{football} \<number>', assuming \UNIX/ command-line conventions, the program works harder. Higher values of \<number> do more calculation and tend to find better chains. For example, the simple greedy algorithm favors Stanford over Harvard by only 781; \.{football}~\.{10} raises this to 1895; the example above corresponds to \.{football}~\.{4000}. @ Here is the general program layout, as seen by the \CEE/ compiler: @^UNIX dependencies@> @p #include "gb_graph.h" /* the standard GraphBase data structures */ #include "gb_games.h" /* the routine that sets up the graph of scores */ #include "gb_flip.h" /* random number generator */ @h@# @<Type declarations@>@; @<Global variables@>@; @<Subroutines@>@; main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ { @<Scan the command-line options@>; @<Set up the graph@>; while(1) { @<Prompt for starting team and goal team; |break| if none given@>; @<Find a chain from |start| to |goal|, and print it@>; } return 0; /* normal exit */ } @ Let's deal with \UNIX/-dependent stuff first. The rest of this program should work without change on any operating system. @^UNIX dependencies@> @<Scan the command-line options@>= if (argc==3 && strcmp(argv[2],"-v")==0) verbose=argc=2; /* secret option */ if (argc==1) width=0; else if (argc==2 && sscanf(argv[1],"%ld",&width)==1) { if (width<0) width=-width; /* a \UNIX/ user might have used a hyphen */ }@+else { fprintf(stderr,"Usage: %s [searchwidth]\n",argv[0]); return -2; } @ @<Glob...@>= long width; /* number of cases examined per stratum */ Graph *g; /* the graph containing score information */ Vertex *u,*v; /* vertices of current interest */ Arc *a; /* arc of current interest */ Vertex *start,*goal; /* teams specified by the user */ long mm; /* counter used only in |verbose| mode */ @ An arc from |u| to |v| in the graph generated by |games| has a |len| field equal to the number of points scored by |u| against |v|. For our purposes we want also a |del| field, which gives the difference between the number of points scored by |u| and the number of points scored by~|v| in that game. @d del a.I /* |del| info appears in utility field |a| of an |Arc| record */ @<Set up the graph@>= g=games(0L,0L,0L,0L,0L,0L,0L,0L); /* this default graph has the data for the entire 1990 season */ if (g==NULL) { fprintf(stderr,"Sorry, can't create the graph! (error code %ld)\n", panic_code); return -1; } for (v=g->vertices;v<g->vertices+g->n;v++) for (a=v->arcs;a;a=a->next) if (a->tip>v) { /* arc |a+1| is the mate of arc |a| iff |a->tip>v| */ a->del=a->len-(a+1)->len; (a+1)->del=-a->del; } @* Terminal interaction. While we're getting trivialities out of the way, we might as well take care of the simple dialog that transpires between this program and the user. @<Prompt for...@>= putchar('\n'); /* make a blank line for visual punctuation */ restart: /* if we avoid this label, the |break| command will be broken */ if ((start=prompt_for_team("Starting"))==NULL) break; if ((goal=prompt_for_team(" Other"))==NULL) goto restart; if (start==goal) { printf(" (Um, please give me the names of two DISTINCT teams.)\n"); goto restart; } @ The user must spell team names exactly as they appear in the file \.{games.dat}. Thus, for example, `\.{Berkeley}' and `\.{Cal}' don't work; it has to be `\.{California}'. Similarly, a person must type `\.{Pennsylvania}' instead of `\.{Penn}', `\.{Nevada-Las} \.{Vegas}' instead of `\.{UNLV}'. A backslash is necessary in `\.{Texas} \.{A\\\&M}'. @<Sub...@>= Vertex *prompt_for_team(s) char *s; /* string used in prompt message */ {@+register char *q; /* current position in |buffer| */ register Vertex *v; /* current vertex being examined in sequential search */ char buffer[30]; /* a line of input */ while (1) { printf("%s team: ",s); fflush(stdout); /* make sure the user sees the prompt */ fgets(buffer,30,stdin); if (buffer[0]=='\n') return NULL; /* the user just hit \<return> */ buffer[29]='\n'; for (q=buffer;*q!='\n';q++) ; /* scan to end of input */ *q='\0'; for (v=g->vertices;v<g->vertices+g->n;v++) if (strcmp(buffer,v->name)==0) return v; /* aha, we found it */ printf(" (Sorry, I don't know any team by that name.)\n"); printf(" (One team I do know is %s...)\n", (g->vertices+gb_unif_rand(g->n))->name); } } @*Greed. This program's primary task is to find the longest possible simple path from |start| to |goal|, using |del| as the length of each arc in the path. This is an NP-complete problem, and the number of possibilities is pretty huge, so the present program is content to use heuristics that are reasonably easy to compute. (Researchers are hereby challenged to come up with better heuristics. Does simulated annealing give good results? How about genetic algorithms?) Perhaps the first approach that comes to mind is a simple ``greedy'' approach in which each step takes the largest possible |del| that doesn't prevent us from eventually getting to |goal|. So that's the method we will implement first. @ @<Find a chain from |start| to |goal|, and print it@>= @<Initialize the allocation of auxiliary memory@>; if (width==0) @<Use a simple-minded greedy algorithm to find a chain from |start| to |goal|@>@; else @<Use a stratified heuristic to find a chain from |start| to |goal|@>; @<Print the solution corresponding to |cur_node|@>; @<Recycle the auxiliary memory used@>; @ We might as well use data structures that are more general than we need, in anticipation of a more complex heuristic that will be implemented later. The set of all possible solutions can be viewed as a backtrack tree in which the branches from each node are the games that can possibly follow that node. We will examine a small part of that gigantic tree. @<Type declarations@>= typedef struct node_struct { Arc *game; /* game from the current team to the next team */ long tot_len; /* accumulated length from |start| to here */ struct node_struct *prev; /* node that gave us the current team */ struct node_struct *next; /* list pointer to node in same stratum (see below) */ } node; @ @<Glob...@>= Area node_storage; /* working storage for heuristic calculations */ node *next_node; /* where the next node is slated to go */ node *bad_node; /* end of current allocation block */ node *cur_node; /* current node of particular interest */ @ @<Initialize the allocation of auxiliary memory@>= next_node=bad_node=NULL; @ @<Subroutines@>= node *new_node(x,d) node *x; /* an old node that the new node will call |prev| */ long d; /* incremental change to |tot_len| */ { if (next_node==bad_node) { next_node=gb_typed_alloc(1000,node,node_storage); if (next_node==NULL) return NULL; /* we're out of space */ bad_node=next_node+1000; } next_node->prev=x; next_node->tot_len=(x?x->tot_len:0)+d; return next_node++; } @ @<Recycle the auxiliary memory used@>= gb_free(node_storage); @ When we're done, |cur_node->game->tip| will be the |goal| vertex, and we can get back to the |start| vertex by following |prev| links from |cur_node|. It looks better to print the answers from |start| to |goal|, so maybe we should have changed our algorithm to go the other way. But let's not worry over trifles. It's easy to change the order of a linked list. The secret is simply to think of the list as a stack, from which we pop all the elements off to another stack; the new stack has the elements in reverse order. @<Print the solution corresponding to |cur_node|@>= next_node=NULL; /* now we'll use |next_node| as top of temporary stack */ do@+{@+register node*t; t=cur_node; cur_node=t->prev; /* pop */ t->prev=next_node; next_node=t; /* push */ }@+while (cur_node); for (v=start;v!=goal;v=u,next_node=next_node->prev) { a=next_node->game; u=a->tip; @<Print the score of game |a| between |v| and |u|@>; printf(" (%+ld)\n",next_node->tot_len); } @ @<Print the score of game |a| between |v| and |u|@>= {@+register long d=a->date; /* date of the game, 0 means Aug 26 */ if (d<=5) printf(" Aug %02ld",d+26); else if (d<=35) printf(" Sep %02ld",d-5); else if (d<=66) printf(" Oct %02ld",d-35); else if (d<=96) printf(" Nov %02ld",d-66); else if (d<=127) printf(" Dec %02ld",d-96); else printf(" Jan 01"); /* |d=128| */ printf(": %s %s %ld, %s %s %ld",v->name,v->nickname,a->len, u->name,u->nickname,a->len-a->del); } @ We can't just move from |v| to any adjacent vertex; we can go only to a vertex from which |goal| can be reached without touching |v| or any other vertex already used on the path from |start|. Furthermore, if the locally best move from |v| is directly to |goal|, we don't want to make that move unless it's our last chance; we can probably do better by making the chain longer. Otherwise, for example, a chain between a team and its worst opponent would consist of only a single game. To keep track of untouchable vertices, we use a utility field called |blocked| in each vertex record. Another utility field, |valid|, will be set to a validation code in each vertex that still leads to the goal. @d blocked u.I @d valid v.V @<Use a simple-minded greedy algorithm to find a chain...@>= { for (v=g->vertices;v<g->vertices+g->n;v++) v->blocked=0,v->valid=NULL; cur_node=NULL; for (v=start;v!=goal;v=cur_node->game->tip) {@+register long d=-10000; register Arc *best_arc; /* arc that achieves |del=d| */ register Arc *last_arc; /* arc that goes directly to |goal| */ v->blocked=1; cur_node=new_node(cur_node,0L); if (cur_node==NULL) { fprintf(stderr,"Oops, there isn't enough memory!\n");@+return -2; } @<Set |u->valid=v| for all |u| to which |v| might now move@>; for (a=v->arcs;a;a=a->next) if (a->del>d && a->tip->valid==v) if (a->tip==goal) last_arc=a; else best_arc=a,d=a->del; cur_node->game=(d==-10000?last_arc:best_arc); /* use |last_arc| as a last resort */ cur_node->tot_len+=cur_node->game->del; } } @ A standard marking algorithm supplies the final missing link in our algorithm. @d link w.V @<Set |u->valid=v| for all |u| to which |v| might now move@>= u=goal; /* |u| will be the top of a stack of nodes to be explored */ u->link=NULL; u->valid=v; do@+{ for (a=u->arcs,u=u->link;a;a=a->next) if (a->tip->blocked==0 && a->tip->valid!=v) { a->tip->valid=v; /* mark |a->tip| reachable from |goal| */ a->tip->link=u; u=a->tip; /* push it on the stack, so that its successors will be marked too */ } }@+while (u); @*Stratified greed. One approach to better chains is the following algorithm, motivated by similar ideas of Pang Chen [Ph.D. thesis, Stanford University, 1989]: @^Chen, Pang-Chieh@> Suppose the nodes of a (possibly huge) backtrack tree are classified into a (fairly small) number of strata, by a function $h$ with the property that $h({\rm child})<h({\rm parent})$ and $h({\rm goal})=0$. Suppose further that we want to find a node $x$ that maximizes a given function~$f(x)$, where it is reasonable to believe that $f$(child) will be relatively large among nodes in a child's stratum only if $f$(parent) is relatively large in the parent's stratum. Then it makes sense to restrict backtracking to, say, the top $w$ nodes of each stratum, ranked by their $f$ values. The greedy algorithm already described is a special case of this general approach, with $w=1$ and with $h(x)=-($length of chain leading to~$x)$. The refined algorithm we are about to describe uses a general value of $w$ and a somewhat more relevant stratification function: Given a node~$x$ of the backtrack tree for longest paths, corresponding to a path from |start| to a certain vertex~$u=u(x)$, we will let $h(x)$ be the number of vertices that lie between |u| and |goal| (in the sense that the simple path from |start| to~|u| can be extended until it passes through such a vertex and then all the way to~|goal|). Here is the top level of the stratified greedy algorithm. We maintain a linked list of nodes for each stratum, that is, for each possible value of~$h$. The number of nodes required is bounded by $w$ times the number of strata. @<Use a strat...@>= { @<Make |list[0]| through |list[n-1]| empty@>; cur_node=NULL; /* |NULL| represents the root of the backtrack tree */ m=g->n-1; /* the highest stratum not yet fully explored */ do@+{ @<Place each child~|x| of |cur_node| into |list[h(x)]|, retaining at most |width| nodes of maximum |tot_len| on each list@>; while (list[m]==NULL) @<Decrease |m| and get ready to explore another list@>; cur_node=list[m]; list[m]=cur_node->next; /* remove a node from highest remaining stratum */ if (verbose) @<Print ``verbose'' info about |cur_node|@>; }@+while (m>0); /* exactly one node should be in |list[0]| (see below) */ } @ The calculation of $h(x)$ is somewhat delicate, and we will defer it for a moment. But the list manipulation is easy, so we can finish it quickly while it's fresh in our minds. @d MAX_N 120 /* the number of teams in \.{games.dat} */ @<Glob...@>= node *list[MAX_N]; /* the best nodes known in given strata */ long size[MAX_N]; /* the number of elements in a given |list| */ long m,h; /* current lists of interest */ node *x; /* a child of |cur_node| */ @ @<Make |list[0]|...@>= for (m=0;m<g->n;m++) list[m]=NULL,size[m]=0; @ The lists are maintained in order by |tot_len|, with the largest |tot_len| value at the end so that we can easily delete the smallest. When |h=0|, we retain only one node instead of~|width| different nodes, because we are interested in only one solution. @<Place node~|x| into |list[h]|, retaining at most |width| nodes of maximum |tot_len|@>= if ((h>0 && size[h]==width) || (h==0 && size[0]>0)) { if (x->tot_len<=list[h]->tot_len) goto done; /* drop node |x| */ list[h]=list[h]->next; /* drop one node from |list[h]| */ }@+else size[h]++; {@+register node *p,*q; /* node in list and its predecessor */ for (p=list[h],q=NULL; p; q=p,p=p->next)@+ if (x->tot_len<=p->tot_len) break; x->next=p; if (q) q->next=x;@+ else list[h]=x; } done:; @ We reverse the list so that large entries will tend to go in first. @<Decrease |m| and get ready to explore another list@>= {@+register node *r=NULL, *s=list[--m], *t; while (s) t=s->next, s->next=r, r=s, s=t; list[m]=r; mm=0; /* |mm| is an index for ``verbose'' printing */ } @ @<Print ``verbose'' info...@>= { cur_node->next=(node*)((++mm<<8)+m); /* pack an ID for this node */ printf("[%lu,%lu]=[%lu,%lu]&%s (%+ld)\n",m,mm,@| cur_node->prev?((unsigned long)cur_node->prev->next)&0xff:0L,@| cur_node->prev?((unsigned long)cur_node->prev->next)>>8:0L,@| cur_node->game->tip->name, cur_node->tot_len); } @ Incidentally, it is plausible to conjecture that the stratified algorithm always beats the simple greedy algorithm, but that conjecture is false. For example, the greedy algorithm is able to rank Harvard over Stanford by 1529, while the stratified algorithm achieves only 1527 when |width=1|. On the other hand, the greedy algorithm often fails miserably; when comparing two Ivy League teams, it doesn't find a way to break out of the Ivy and Patriot Leagues. @*Bicomponents revisited. How difficult is it to compute the function $h$? Given a connected graph~$G$ with two distinguished vertices $u$ and~$v$, we want to count the number of vertices that might appear on a simple path from $u$ to~$v$. (This is {\sl not\/} the same as the number of vertices reachable from both $u$ and~$v$. For example, consider a ``claw'' graph with four vertices $\{u,v,w,x\}$ and with edges only from $x$ to the other three vertices; in this graph $w$ is reachable from $u$ and~$v$, but it is not on any simple path between them.) The best way to solve this problem is probably to compute the bicomponents of~$G$, or least to compute some of them. Another demo program, {\sc BOOK\_\kern.05emCOMPONENTS}, explains the relevant theory in some detail, and we will assume familiarity with that algorithm in the present discussion. Let us imagine extending $G$ to a slightly larger graph $G^+$ by adding a dummy vertex~$o$ that is adjacent only to $v$. Suppose we determine the bicomponents of $G^+$ by depth-first search starting at~$o$. These bicomponents form a tree rooted at the bicomponent that contains just $o$ and~$v$. The number of vertices on paths between $u$ and~$v$, not counting $v$ itself, is then the number of vertices in the bicomponent containing~$u$ and in any other bicomponents between that one and the root. Strictly speaking, each articulation point belongs to two or more bicomponents. But we will assign each articulation point to its bicomponent that is nearest the root of the tree; then the vertices of each bicomponent are precisely the vertices output in bursts by the depth-first procedure. The bicomponents we want to enumerate are $B_1$, $B_2$, \dots,~$B_k$, where $B_1$ is the bicomponent containing~$u$ and $B_{j+1}$ is the bicomponent containing the articulation point associated with~$B_j$; we stop at~$B_k$ when its associated articulation point is~$v$. (Often $k=1$.) The ``children'' of a given graph~$G$ are obtained by removing vertex~$u$ and by considering paths from $u'$ to~$v$, where $u'$ is a vertex formerly adjacent to~$u$; thus $u'$ is either in~$B_1$ or it is $B_1$'s associated articulation point. Removing $u$ will, in general, split $B_1$ into a tree of smaller bicomponents, but $B_2,\ldots,B_k$ will be unaffected. The implementation below does not take full advantage of this observation, because the amount of memory required to avoid recomputation would probably be prohibitive. @ The following program is copied almost verbatim from {\sc BOOK\_\kern.05emCOMPONENTS}. Instead of repeating the commentary that appears there, we will mention only the significant differences. One difference is that we start the depth-first search at a definite place, the |goal|. @<Place each child~|x| of |cur_node| into |list[h(x)]|, retaining at most |width| nodes of maximum |tot_len| on each list@>= @<Make all vertices unseen and all arcs untagged, except for vertices that have already been used in steps leading up to |cur_node|@>; @<Perform a depth-first search with |goal| as the root, finding bicomponents and determining the number of vertices accessible between any given vertex and |goal|@>; for (a=(cur_node? cur_node->game->tip: start)->arcs; a; a=a->next) if ((u=a->tip)->untagged==NULL) { /* |goal| is reachable from |u| */ x=new_node(cur_node,a->del); if (x==NULL) { fprintf(stderr,"Oops, there isn't enough memory!\n");@+return -3; } x->game=a; @<Set |h| to the number of vertices on paths between |u| and |goal|@>; @<Place node...@>; } @ Setting the |rank| field of a vertex to infinity before beginning a depth-first search is tantamount to removing that vertex from the graph, because it tells the algorithm not to look further at such a vertex. @d rank z.I /* when was this vertex first seen? */ @d parent u.V /* who told me about this vertex? */ @d untagged x.A /* what is its first untagged arc? */ @d min v.V /* how low in the tree can we jump from its mature descendants? */ @<Make all vertices unseen and all arcs untagged, except for vertices that have already been used in steps leading up to |cur_node|@>= for (v=g->vertices; v<g->vertices+g->n; v++) { v->rank=0; v->untagged=v->arcs; } for (x=cur_node;x;x=x->prev) x->game->tip->rank=g->n; /* ``infinite'' rank (or close enough) */ start->rank=g->n; nn=0; active_stack=settled_stack=NULL; @ @<Glob...@>= Vertex * active_stack; /* the top of the stack of active vertices */ Vertex *settled_stack; /* the top of the stack of bicomponents found */ long nn; /* the number of vertices that have been seen */ Vertex dummy; /* imaginary parent of |goal|; its |rank| is zero */ @ The |settled_stack| will contain a list of all bicomponents in the opposite order from which they are discovered. This is the order we'll need later for computing the |h| function in each bicomponent. @<Perform a depth-first search...@>= { v=goal; v->parent=&dummy; @<Make vertex |v| active@>; do @<Explore one step from the current vertex~|v|, possibly moving to another current vertex and calling~it~|v|@>@; while (v!=&dummy); @<Use |settled_stack| to put the mutual reachability count for each vertex |u| in |u->parent->rank|@>; } @ @<Make vertex |v| active@>= v->rank=++nn; v->link=active_stack; active_stack=v; v->min=v->parent; @ @<Explore one step from the current vertex~|v|, possibly moving to another current vertex and calling~it~|v|@>= {@+register Vertex *u; /* a vertex adjacent to |v| */ register Arc *a=v->untagged; /* |v|'s first remaining untagged arc, if any */ if (a) { u=a->tip; v->untagged = a->next; /* tag the arc from |v| to |u| */ if (u->rank) { /* we've seen |u| already */ if (u->rank < v->min->rank) v->min=u; /* non-tree arc, just update |v->min| */ }@+else { /* |u| is presently unseen */ u->parent = v; /* the arc from |v| to |u| is a new tree arc */ v = u; /* |u| will now be the current vertex */ @<Make vertex |v| active@>; } }@+else { /* all arcs from |v| are tagged, so |v| matures */ u=v->parent; /* prepare to backtrack in the tree */ if (v->min==u) @<Remove |v| and all its successors on the active stack from the tree, and report them as a bicomponent of the graph together with~|u|@>@; else /* the arc from |u| to |v| has just matured, making |v->min| visible from |u| */@, if (v->min->rank < u->min->rank) u->min=v->min; v=u; /* the former parent of |v| is the new current vertex |v| */ } } @ When a bicomponent is found, we reset the |parent| field of each vertex so that, afterwards, two vertices will belong to the same bicomponent if and only if they have the same |parent|. (This trick was not used in {\sc BOOK\_\kern.05emCOMPONENTS}, but it does appear in the similar algorithm of {\sc ROGET\_\,COMPONENTS}.) The new parent, |v|, will represent that bicomponent in subsequent computation; we put it onto |settled_stack|. We also reset |v->rank| to be the bicomponent's size, plus a constant large enough to keep the algorithm from getting confused. (Vertex~|u| might still have untagged arcs leading into this bicomponent; we need to keep the ranks at least as big as the rank of |u->min|.) Notice that |v->min| is |u|, the articulation point associated with this bicomponent. Later the |rank| field will contain the sum of all counts between here and the root. We don't have to do anything when |v==goal|; the trivial root bicomponent always comes out last. @<Remove |v| and all its successors on the active stack...@>= {@+if (v!=goal) {@+register Vertex *t; /* runs through the vertices of the new bicomponent */ long c=0; /* the number of vertices removed */ t=active_stack; while (t!=v) { c++; t->parent=v; t=t->link; } active_stack=v->link; v->parent=v; v->rank=c+g->n; /* the true component size is |c+1| */ v->link=settled_stack; settled_stack=v; } } @ So here's how we sum the ranks. When we get to this step, the \\{settled} stack contains all bicomponent representatives except |goal| itself. @<Use |settled_stack| to put the mutual reachability count for each vertex |u| in |u->parent->rank|@>= while (settled_stack) { v=settled_stack; settled_stack=v->link; v->rank+=v->min->parent->rank+1-g->n; } /* note that |goal->parent->rank=0| */ @ And here's the last piece of the puzzle. @<Set |h| to the number of vertices on paths between |u| and |goal|@>= h=u->parent->rank; @* Index. Finally, here's a list that shows where the identifiers of this program are defined and used. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������girth.w���������������������������������������������������������������������������������������������0000444�0001750�0001750�00000033561�06236451463�010524� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{GIRTH} \let\==\equiv % congruence sign \prerequisite{GB\_\,RAMAN} @* Introduction. This demonstration program uses graphs constructed by the |raman| procedure in the {\sc GB\_\,RAMAN} module to produce an interactive program called \.{girth}, which computes the girth and diameter of a class of Ramanujan graphs. The girth of a graph is the length of its shortest cycle; the diameter is the maximum length of a shortest path between two vertices. A Ramanujan graph is a connected, undirected graph in which every vertex @^Ramanujan graphs@> has degree~|p+1|, with the property that every eigenvalue of its adjacency matrix is either $\pm(p+1)$ or has absolute value $\le2\sqrt{\mathstrut p}$. Exact values for the girth are of interest because the bipartite graphs produced by |raman| apparently have larger girth than any other known family of regular graphs, even if we consider graphs whose existence is known only by nonconstructive methods, except for the cubic ``sextet'' graphs of Biggs, Hoare, and Weiss [{\sl Combinatorica\/ \bf3} (1983), @^Biggs, Norman L.@> @^Hoare, M. J.@> @^Weiss, Alfred@> 153--165; {\bf4} (1984), 241--245]. Exact values for the diameter are of interest because the diameter of any Ramanujan graph is at most twice the minimum possible diameter of any regular graph. The program will prompt you for two numbers, |p| and |q|. These should be distinct prime numbers, not too large, with |q>2|. A graph is constructed in which each vertex has degree~|p+1|. The number of vertices is $(q^3-q)/2$ if |p| is a quadratic residue modulo~|q|, or $q^3-q$ if |p| is not a quadratic residue. In the latter case, the graph is bipartite and it is known to have rather large girth. If |p=2|, the value of |q| is further restricted to be of the form $104k+(1,3,9,17,25,27,35,43,49,\allowbreak51,75,81)$. This means that the only feasible values of |q| to go with |p=2| are probably 3, 17, and 43; the next case, |q=107|, would generate a bipartite graph with 1,224,936 vertices and 3,674,808 arcs, thus requiring approximately 113 megabytes of memory (not to mention a nontrivial amount of computer time). If you want to compute the girth and diameter of Ramanujan graphs for large |p| and/or~|q|, much better methods are available based on number theory; the present program is merely a demonstration of how to interface with the output of |raman|. Incidentally, the graph for |p=2| and |q=43| turns out to have 79464 vertices, girth 20, and diameter~22. The program will examine the graph and compute its girth and its diameter, then will prompt you for another choice of |p| and |q|. @ Here is the general layout of this program, as seen by the \CEE/ compiler: @p #include "gb_graph.h" /* the standard GraphBase data structures */ #include "gb_raman.h" /* Ramanujan graph generator */ @h@# @<Global variables@>@; main() { printf( "This program explores the girth and diameter of Ramanujan graphs.\n"); printf("The bipartite graphs have q^3-q vertices, and the non-bipartite\n"); printf("graphs have half that number. Each vertex has degree p+1.\n"); printf("Both p and q should be odd prime numbers;\n"); printf(" or you can try p = 2 with q = 17 or 43.\n"); while (1) { @<Prompt the user for |p| and |q|; |break| if unsuccessful@>; g=raman(p,q,0L,0L); if (g==NULL) @<Explain that the graph could not be constructed@>@; else { @<Print the theoretical bounds on girth and diameter of |g|@>; @<Compute and print the true girth and diameter of |g|@>; gb_recycle(g); } } return 0; /* normal exit */ } @ @<Global...@>= Graph *g; /* the current Ramanujan graph */ long p; /* the branching factor (degree minus one) */ long q; /* cube root of the graph size */ char buffer[16]; /* place to collect what the user types */ @ @d prompt(s) {@+printf(s);@+fflush(stdout); /* make sure the user sees the prompt */ if (fgets(buffer,15,stdin)==NULL) break;@+} @<Prompt...@>= prompt("\nChoose a branching factor, p: "); if (sscanf(buffer,"%ld",&p)!=1) break; prompt("OK, now choose the cube root of graph size, q: "); if (sscanf(buffer,"%ld",&q)!=1) break; @ @<Explain that the graph could not be constructed@>= printf(" Sorry, I couldn't make that graph (%s).\n",@| panic_code==very_bad_specs? "q is out of range":@| panic_code==very_bad_specs+1? "p is out of range":@| panic_code==bad_specs+5? "q is too big":@| panic_code==bad_specs+6? "p is too big":@| panic_code==bad_specs+1? "q isn't prime":@| panic_code==bad_specs+7? "p isn't prime":@| panic_code==bad_specs+3? "p is a multiple of q":@| panic_code==bad_specs+2? "q isn't compatible with p=2":@| "not enough memory"); @* Bounds. The theory of Ramanujan graphs allows us to predict the girth and diameter to within a factor of 2~or~so. In the first place, we can easily derive an upper bound on the girth and a lower bound on the diameter, valid for any $n$-vertex regular graph of degree~$p+1$. Such a graph has at most $(p+1)p^{k-1}$ points at distance~$k$ from any given vertex; this implies a lower bound on the diameter~$d$: $$1+(p+1)+(p+1)p+(p+1)p^2+\cdots+(p+1)p^{d-1}\;\ge\;n.$$ Similarly, if the girth $g$ is odd, say $g=2k+1$, the points at distance~$\le k$ from any vertex must be distinct, so we have $$1+(p+1)+(p+1)p+(p+1)p^2+\cdots+(p+1)p^{k-1}\;\le\;n;$$ and if $g=2k+2$, at least $p^k$ further points must exist at distance $k+1$, because the $(p+1)p^k$ paths of length $k+1$ can end at a particular vertex at most $p+1$ times. Thus $$1+(p+1)+(p+1)p+(p+1)p^2+\cdots+(p+1)p^{k-1}+p^k\;\le\;n$$ when the girth is even. In the following code we let $|pp|=p^{dl}$ and $s=1+(p+1)+\cdots+(p+1)p^{dl}$. @<Compute the ``trivial'' bounds |gu| and |dl| on girth and diameter@>= s=p+2;@+dl=1;@+pp=p;@+gu=3; while (s<n) { s+=pp; if (s<=n) gu++; dl++; pp*=p; s+=pp; if (s<=n) gu++; } @ When |p>2|, we can use the theory of integral quaternions to derive a lower bound on the girth of the graphs produced by |raman|. A path of length~$g$ from a vertex to itself exists if and only if there is an integral quaternion $\alpha=a_0+a_1i+a_2j+a_3k$ of norm $p^g$ such that the $a$'s are not all multiples of~$p$, while $a_1$, $a_2$, and $a_3$ are multiples of~$q$ and $a_0\not\=a_1\=a_2\=a_3$ (mod~2). This means we have integers $(a_0,a_1,a_2,a_3)$ with $$a_0^2+a_1^2+a_2^2+a_3^2=p^{\,g},$$ satisfying the stated properties mod~$q$ and mod~2. If $a_1$, $a_2$, and $a_3$ are even, they cannot all be zero so we must have $p^{\,g}\ge1+4q^2$; if they are odd, we must have $p^{\,g}\ge4+3q^2$. (The latter is possible only when $g$ is odd and $p\bmod4=3$.) Since $n$ is roughly proportional to~$q^3$, this means $g$ must be at least about ${2\over3}\log_p n$. Thus $g$~isn't too much less than the maximum girth possible in any regular graph, which we have shown is at most about $2\log_p n$. When the graph is bipartite we can, in fact, prove that $g$ is approximately ${4\over3}\log_p n$. The bipartite case occurs if and only if $p$ is not a quadratic residue modulo~|q|; hence the number~$g$ in the previous paragraph must be even, say $g=2r$. Then $p^{\,g}\bmod4=1$, and $a_0$ must be odd. The congruence $a_0^2\=p^{2r}$ (mod~$q^2$) implies that $a_0\=\pm p^r$, because all numbers relatively prime to $q^2$ are powers of a primitive root. We can assume without loss of generality that $a_0=p^r-2mq^2$, where $0<m<p^r/q^2$; it follows in particular that $p^r>q^2$. Conversely, if $p^r-q^2$ can be written as a sum of three squares $b_1^2+b_2^2+b_3^2$, then $p^{2r}=(p^r-2q^2)^2+(2b_1q)^2+(2b_2q)^2+(2b_3q)^2$ is a representation of the required type. If $p^r-q^2$ is a positive integer that cannot be represented as a sum of three squares, a well-known theorem of Legendre tells us that $p^r-q^2=4^ts$, where $s\=7$ (mod~8). Since $p$ and $q$ are odd, we have $t\ge1$; hence $p^r-2q^2$ is odd. If $p^r-2q^2$ is a positive odd integer, Legendre's theorem tells us that we can write $2p^r-4q^2=b_1^2+b_2^2+b_3^2$; hence $p^{2r}=(p^r-4q^2)^2+ (2b_1q)^2+(2b_2q)^2+(2b_3q)^2$. We conclude that the girth is either $2\lceil\log_pq^2\rceil$ or $2\lceil\log_p2q^2\rceil$. (This explicit calculation, which makes our program for calculating the girth unnecessary or at best redundant in the bipartite case, is due to G. A. Margulis and, independently, to @^Margulis, Grigori{\u\i} Aleksandrovich@> @^Biggs, Norman L.@> @^Boshier, A. G.@> Biggs and Boshier [{\sl Journal of Combinatorial Theory\/ \bf B49} (1990), 190--194].) A girth of 1 or 2 can occur, since these graphs might have self-loops or multiple edges if |p| is sufficiently large. @<Compute a lower bound |gl| on the girth@>= if (bipartite) {@+long b=q*q; for (gl=1,pp=p;pp<=b;gl++,pp*=p) ; /* iterate until $p^{\,g}>q^2$ */ gl+=gl; }@+else {@+long b1=1+4*q*q, b2=4+3*q*q; /* bounds on $p^{\,g}$ */ for (gl=1,pp=p;pp<b1;gl++,pp*=p) { if (pp>=b2 && (gl&1) && (p&2)) break; } } @ Upper bounds on the diameter of any Ramanujan graph can be derived as shown in the paper by Lubotzky, Phillips, and Sarnak in @^Lubotzky, Alexander@> @^Phillips, Ralph Saul@> @^Sarnak, Peter@> {\sl Combinatorica \bf8} (1988), page~275. (However, a slight correction to their proof is necessary---their parameter~$l$ should be~odd when $x$ and~$y$ lie in different parts of a bipartite graph.) Their argument demonstrates that $p^{(d-1)/2}<2n$ in the nonbipartite case and $p^{(d-2)/2}<n$ in the bipartite case; therefore we obtain the upper bound $d\le 2\log_p n+O(1)$, which is about twice the lower bound that holds in an arbitrary regular graph. @<Compute an upper bound |du| on the diameter@>= {@+long nn=(bipartite? n: 2*n); for (du=0,pp=1; pp<nn; du+=2,pp*=p) ; @<Decrease |du| by 1, if $|pp|/|nn|\ge\sqrt p$@>; if (bipartite) du++; } @ Floating point arithmetic might not be accurate enough for the test required in this section. We avoid it by using an all-integer method analogous to Euclid's algorithm, based on the continued fraction for $\sqrt p$ [{\sl Seminumerical Algorithms}, exercise 4.5.3--12]. In the loop here we want to compare |nn/pp| to $(\sqrt p+a)/b$, where $\sqrt p+a >b>0$ and $p-a^2$ is a multiple of~$b$. @<Decrease |du|...@>= {@+long qq=pp/nn; if (qq*qq>p) du--; else if ((qq+1)*(qq+1)>p) { /* $|qq|=\lfloor\sqrt p\,\rfloor$ */ long aa=qq, bb=p-aa*aa, parity=0; pp-=qq*nn; while (1) { long x=(aa+qq)/bb, y=nn-x*pp; if (y<=0) break; aa=bb*x-aa; /* now $0<|aa|<\sqrt p$ */ bb=(p-aa*aa)/bb; nn=pp;@+pp=y; parity^=1; } if (!parity) du--; } } @ @<Print the theoretical bounds on girth and diameter of |g|@>= n=g->n; if (n==(q+1)*q*(q-1)) bipartite=1; else bipartite=0; printf( "The graph has %ld vertices, each of degree %ld, and it is %sbipartite.\n", n,p+1,bipartite? "": "not "); @<Compute the ``trivial'' bounds |gu| and |dl| on girth and diameter@>; printf("Any such graph must have diameter >= %ld and girth <= %ld;\n", dl,gu); @<Compute an upper bound |du| on the diameter@>; printf("theoretical considerations tell us that this one's diameter is <= %ld", du); if (p==2) printf(".\n"); else { @<Compute a lower bound |gl| on the girth@>; printf(",\nand its girth is >= %ld.\n",gl); } @ We had better declare all the variables we've been using so freely. @<Global...@>= long gl,gu,dl,du; /* theoretical bounds */ long pp; /* power of $p$ */ long s; /* accumulated sum */ long n; /* number of vertices */ char bipartite; /* is the graph bipartite? */ @*Breadth-first search. The graphs produced by |raman| are symmetrical, in the sense that there is an automorphism taking any vertex into any other. Each vertex $V$ and each edge $P$ corresponds to a $2\times2$ matrix, and the path $P_1P_2\ldots P_k$ leading from vertex~$V$ to vertex $VP_1P_2\ldots P_k$ has the same properties as the path leading from vertex~$U$ to vertex $UP_1P_2\ldots P_k$. Therefore we can find the girth and the diameter by starting at any vertex $v_0$. We compute the number of points at distance $k$ from $v_0$ for all $k$, by explicitly forming a linked list of all such points. Utility field |link| is used for the links. The lists terminate with a non-null |sentinel| value, so that we can also use the condition |link==NULL| to tell if a vertex has been encountered before. Another utility field, |dist|, contains the distance from the starting point, and |back| points to a vertex one step closer. @d link w.V /* the field where we store links, initially |NULL| */ @d dist v.I /* the field where we store distances, initially 0 */ @d back u.V /* the field where we store backpointers, initially |NULL| */ @<Compute and print the true girth and diameter of |g|@>= printf("Starting at any given vertex, there are\n"); {@+long k; /* current distance being generated */ long c; /* how many we've seen so far at this distance */ register Vertex *v; /* current vertex in list at distance $k-1$ */ register Vertex *u; /* head of list for distance $k$ */ Vertex *sentinel=g->vertices+n; /* nonzero link at end of lists */ long girth=999; /* length of smallest cycle found, initially infinite */ k=0; u=g->vertices; u->link=sentinel; c=1; while (c) { for (v=u,u=sentinel,c=0,k++;v!=sentinel;v=v->link) @<Place all vertices adjacent to |v| onto list |u|, unless they've been encountered before, increasing |c| whenever the list grows@>; printf("%8ld vertices at distance %ld%s\n", c, k, c>0? ",": "."); } printf("So the diameter is %ld, and the girth is %ld.\n",k-1,girth); } @ @<Place all...@>= {@+register Arc *a; for (a=v->arcs;a;a=a->next) {@+register Vertex *w; /* vertex adjacent to |v| */ w=a->tip; if (w->link==NULL) { w->link=u; w->dist=k; w->back=v; u=w; c++; }@+else if (w->dist+k<girth && w!=v->back) girth=w->dist+k; } } @* Index. Finally, here's a list that shows where the identifiers of this program are defined and used. �����������������������������������������������������������������������������������������������������������������������������������������������ladders.w�������������������������������������������������������������������������������������������0000444�0001750�0001750�00000037073�07223146271�011022� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{LADDERS} \prerequisites{GB\_WORDS}{GB\_\,DIJK} @* Introduction. This demonstration program uses graphs constructed by the {\sc GB\_WORDS} module to produce an interactive program called \.{ladders}, which finds shortest paths between two given five-letter words of English. The program assumes that \UNIX/ conventions are being used. Some code in sections listed under `\UNIX/ dependencies' in the index might need to change if this program is ported to other operating systems. \def\<#1>{$\langle${\rm#1}$\rangle$} To run the program under \UNIX/, say `\.{ladders} \<options>', where \<options> consists of zero or more of the following specifications in any order: {\narrower \def\\#1 {\smallskip\noindent \hbox to 6em{\tt#1\hfill}\hangindent 8em\hangafter1 } \\-v Verbosely print all words encountered during the shortest-path computation, showing also their distances from the goal word. \\-a Use alphabetic distance instead of considering adjacent words to be one unit apart; for example, the alphabetic distance from `\.{words}' to `\.{woods}' is~3, because `\.r' is three places from `\.o' in the alphabet. \\-f Use distance based on frequency (see below), instead of considering adjacent words to be one unit apart. This option is ignored if either \.{-a} or \.{-r} has been specified. \\-h Use a lower-bound heuristic to shorten the search (see below). This option is ignored if option \.{-f} has been selected. \\-e Echo the input to the output (useful if input comes from a file instead of from the terminal). \\-n\<number> Limit the graph to the |n| most common English words, where |n| is the given \<number>. \\-r\<number> Limit the graph to \<number> randomly selected words. This option is incompatible with~\.{-n}. \\-s\<number> Use \<number> instead of 0 as the seed for random numbers, to get different random samples or to explore words of equal frequency in a different order. \smallskip} \noindent Option \.{-f} assigns a cost of 0 to the most common words and a cost of 16 to the least common words; a cost between 0 and~16 is assigned to words of intermediate frequency. The word ladders that are found will then have minimum total cost by this criterion. Experience shows that the \.{-f} option tends to give the ``friendliest,'' most intuitively appealing ladders. \smallskip Option \.{-h} attempts to focus the search by giving priority to words that are near the goal. (More precisely, it modifies distances between adjacent words by using a heuristic function $\\{hh}(v)$, which would be the shortest possible distance between $v$ and the goal if every five-letter combination happened to be an English word.) The {\sc GB\_\,DIJK} module explains more about such heuristics; this option is most interesting to watch when used in conjunction with \.{-v}. @ The program will prompt you for a starting word. If you simply type \<return>, it exits; otherwise you should enter a five-letter word (with no uppercase letters) before typing \<return>. Then the program will prompt you for a goal word. If you simply type \<return> at this point, it will go back and ask for a new starting word; otherwise you should specify another five-letter word. Then the program will find and display an optimal word ladder from the start to the goal, if there is a path from one to the other that changes only one letter at a time. And then you have a chance to start all over again, with another starting word. The start and goal words need not be present in the program's graph of ``known'' words. They are temporarily added to that graph, but removed again whenever new start and goal words are given. (Thus you can go from \.{sturm} to \.{drang} even though those words aren't English.) If the \.{-f} option is being used, the cost of the goal word will be 20 when it is not in the program's dictionary. @ Here is the general layout of this program, as seen by the \CEE/ compiler: @^UNIX dependencies@> @p #include <ctype.h> /* system file for character types */ #include "gb_graph.h" /* the standard GraphBase data structures */ #include "gb_words.h" /* routines for five-letter word graphs */ #include "gb_dijk.h" /* routines for shortest paths */ @h@# @<Global variables@>@; @<Subroutines@>@; main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ { @<Scan the command-line options@>; @<Set up the graph of words@>; while(1) { @<Prompt for starting word and goal word; |break| if none given@>; @<Find a minimal ladder from |start| to |goal|, if one exists, and print it@>; } return 0; /* normal exit */ } @* Parsing the options. Let's get the \UNIX/ command-line junk out of the way first, so that we can concentrate on meatier stuff. Our job in this part of the program is to see if the default value zero of external variable |verbose| should change, and/or if the default values of any of the following internal variables should change: @<Global variables@>= char alph=0; /* nonzero if the alphabetic distance option is selected */ char freq=0; /* nonzero if the frequency-based distance option is selected */ char heur=0; /* nonzero if the heuristic search option is selected */ char echo=0; /* nonzero if the input-echo option is selected */ unsigned long n=0; /* maximum number of words in the graph (0 means infinity) */ char randm=0; /* nonzero if we will ignore the weight of words */ long seed=0; /* seed for random number generator */ @ @<Scan the command-line options@>= while (--argc) { @^UNIX dependencies@> if (strcmp(argv[argc],"-v")==0) verbose=1; else if (strcmp(argv[argc],"-a")==0) alph=1; else if (strcmp(argv[argc],"-f")==0) freq=1; else if (strcmp(argv[argc],"-h")==0) heur=1; else if (strcmp(argv[argc],"-e")==0) echo=1; else if (sscanf(argv[argc],"-n%lu",&n)==1) randm=0; else if (sscanf(argv[argc],"-r%lu",&n)==1) randm=1; else if (sscanf(argv[argc],"-s%ld",&seed)==1) ; else { fprintf(stderr,"Usage: %s [-v][-a][-f][-h][-e][-nN][-rN][-sN]\n",argv[0]); return -2; } } if (alph || randm) freq=0; if (freq) heur=0; @*Creating the graph. The GraphBase |words| procedure will produce the five-letter words we want, organized in a graph structure. @d quit_if(x,c) if (x) { fprintf(stderr, "Sorry, I couldn't build a dictionary (trouble code %ld)!\n",c); return c; } @<Set up the graph of words@>= g=words(n,(randm? zero_vector: NULL), 0L,seed); quit_if(g==NULL,panic_code); @<Confirm the options selected@>; @<Modify the edge lengths, if the |alph| or |freq| option was selected@>; @<Modify the priority queue algorithm, if unequal edge lengths are possible@>; @ @<Glob...@>= Graph *g; /* graph created by |words| */ long zero_vector[9]; /* weights to use when ignoring all frequency information */ @ The actual number of words might be decreased to the size of the GraphBase dictionary, so we wait until the graph is generated before confirming the user-selected options. @<Confirm the options selected@>= if (verbose) { if (alph) printf("(alphabetic distance selected)\n"); if (freq) printf("(frequency-based distances selected)\n"); if (heur) printf("(lowerbound heuristic will be used to focus the search)\n"); if (randm) printf("(random selection of %ld words with seed %ld)\n", g->n,seed); else printf("(the graph has %ld words)\n",g->n); } @ The edges in a |words| graph normally have length 1, so we must change them if the user has selected |alph| or |freq|. The character position in which adjacent words differ is recorded in the |loc| field of each arc. The frequency of a word is stored in the |weight| field of its vertex. @d a_dist(k) (*(p+k)<*(q+k)? *(q+k)-*(p+k): *(p+k)-*(q+k)) @<Modify the edge lengths, if the |alph| or |freq| option was selected@>= if (alph) {@+register Vertex *u; for (u=g->vertices+g->n-1; u>=g->vertices; u--) {@+register Arc *a; register char *p=u->name; for (a=u->arcs; a; a=a->next) {@+register char *q=a->tip->name; a->len = a_dist(a->loc); } } }@+else if (freq) {@+register Vertex *u; for (u=g->vertices+g->n-1; u>=g->vertices; u--) {@+register Arc *a; for (a=u->arcs; a; a=a->next) a->len = freq_cost(a->tip); } } @ The default priority queue algorithm of |dijkstra| is quite efficient when all edge lengths are~1. Otherwise we change it to the alternative method that works best for edge lengths less than~128. @<Modify the priority queue algorithm...@>= if (alph || freq || heur) { init_queue=init_128; del_min=del_128; enqueue=enq_128; requeue=req_128; } @ The frequency has been computed with the default weights explained in the documentation of |words|; it is usually less than $2^{16}$. A word whose frequency is 0 costs~16; a word whose frequency is 1 costs~15; a word whose frequency is 2 or 3 costs~14; and the costs keep decreasing by~1 as the frequency doubles, until we reach a cost of~0. @<Sub...@>= long freq_cost(v) Vertex *v; {@+register long acc=v->weight; /* the frequency, to be shifted right */ register long k=16; while (acc) k--, acc>>=1; return (k<0? 0: k); } @* Minimal ladders. The guts of this program is a routine to compute shortest paths between two given words, |start| and |goal|. The |dijkstra| procedure does this, in any graph with nonnegative arc lengths. The only complication we need to deal with here is that |start| and |goal| might not themselves be present in the graph. In that case we want to insert them, albeit temporarily. The conventions of {\sc GB\_\,GRAPH} allow us to do the desired augmentation by creating a new graph |gg| whose vertices are borrowed from~|g|. The graph~|g| has space for two more vertices (actually for four), and any new memory blocks allocated for the additional arcs present in~|gg| will be freed later by the operation |gb_recycle(gg)| without confusion. @<Glob...@>= Graph *gg; /* clone of |g| with possible additional words */ char start[6], goal[6]; /* \.{words} dear to the user's \.{heart}, plus |'\0'| */ Vertex *uu, *vv; /* start and goal vertices in |gg| */ @ @<Find a minimal ladder from |start| to |goal|...@>= @<Build the amplified graph |gg|@>; @<Let |dijkstra| do the hard work@>; @<Print the answer@>; @<Remove all traces of |gg|@>; @ @<Build the amplified graph |gg|@>= gg=gb_new_graph(0L); quit_if(gg==NULL,no_room+5); /* out of memory */ gg->vertices = g->vertices; gg->n = g->n; @<Put the |start| word into |gg|, and let |uu| point to it@>; @<Put the |goal| word into |gg|, and let |vv| point to it@>; if (gg->n==g->n+2) @<Check if |start| is adjacent to |goal|@>; quit_if(gb_trouble_code,no_room+6); /* out of memory */ @ The |find_word| procedure returns |NULL| if it can't find the given word in the graph just constructed by |words|. In that case it has applied its second argument to every adjacent word. Hence the program logic here does everything needed to add a new vertex to~|gg| when necessary. @<Put the |start| word into |gg|, and let |uu| point to it@>= (gg->vertices+gg->n)->name = start; /* a tentative new vertex */ uu=find_word(start,plant_new_edge); if (!uu) uu = gg->vertices + gg->n++; /* recognize the new vertex and refer to it */ @ @<Put the |goal|...@>= if (strncmp(start,goal,5)==0) vv=uu; /* avoid inserting a word twice */ else { (gg->vertices+gg->n)->name = goal; /* a tentative new vertex */ vv=find_word(goal,plant_new_edge); if (!vv) vv = gg->vertices + gg->n++; /* recognize the new vertex and refer to it */ } @ The |alph_dist| subroutine calculates the alphabetic distance between arbitrary five-letter words, whether they are adjacent or not. @<Sub...@>= long alph_dist(p,q) register char *p, *q; { return a_dist(0)+a_dist(1)+a_dist(2)+a_dist(3)+a_dist(4); } @ @<Sub...@>= void plant_new_edge(v) Vertex *v; {@+Vertex *u=gg->vertices+gg->n; /* the new edge runs from |u| to |v| */ gb_new_edge(u,v,1L); /* we have $u>v$, hence |v->arcs=u->arcs-1| */ if (alph) u->arcs->len=(u->arcs-1)->len=alph_dist(u->name,v->name); else if (freq) { u->arcs->len=freq_cost(v); /* adjust the arc length from |u| to |v| */ (u->arcs-1)->len=20; /* adjust the arc length from |v| to |u| */ } } @ There's a bug in the above logic that could be embarrassing, although it will come up only when a user is trying to be clever: The |find_word| routine knows only the words of~|g|, so it will fail to make any direct connection between |start| and |goal| if they happen to be adjacent to each other yet not in the original graph. We had better fix this, otherwise the computer will look stupid. @<Check if |start|...@>= if (hamm_dist(start,goal)==1) { gg->n--; /* temporarily pretend |vv| hasn't been added yet */ plant_new_edge(uu); /* make |vv| adjacent to |uu| */ gg->n++; /* and recognize it again */ } @ The Hamming distance between words is the number of character positions @^Hamming, Richard Wesley, distance@> in which they differ. @d h_dist(k) (*(p+k)==*(q+k)? 0: 1) @<Sub...@>= long hamm_dist(p,q) register char *p, *q; { return h_dist(0)+h_dist(1)+h_dist(2)+h_dist(3)+h_dist(4); } @ OK, now we've got a graph in which |dijkstra| can operate. @<Let |dijkstra| do the hard work@>= if (!heur) min_dist=dijkstra(uu,vv,gg,NULL); else if (alph) min_dist=dijkstra(uu,vv,gg,alph_heur); else min_dist=dijkstra(uu,vv,gg,hamm_heur); @ @<Sub...@>= long alph_heur(v) Vertex *v; {@+return alph_dist(v->name,goal);@+} @# long hamm_heur(v) Vertex *v; {@+return hamm_dist(v->name,goal);@+} @ @<Glob...@>= long min_dist; /* length of the shortest ladder */ @ @<Print the answer@>= if (min_dist<0) printf("Sorry, there's no ladder from %s to %s.\n",start,goal); else print_dijkstra_result(vv); @ Finally, we have to clean up our tracks. It's easy to remove all arcs from the new vertices of~|gg| to the old vertices of~|g|; it's a bit trickier to remove the arcs from old to new. The loop here will also remove arcs properly between start and goal vertices, if they both belong to |gg| not~|g|. @<Remove all traces of |gg|@>= for (uu=g->vertices+gg->n-1; uu>=g->vertices+g->n; uu--) {@+register Arc *a; for (a=uu->arcs; a; a=a->next) { vv=a->tip; /* now |vv->arcs==a-1|, since arcs for edges come in pairs */ vv->arcs=vv->arcs->next; } uu->arcs=NULL; /* we needn't clear |uu->name| */ } gb_recycle(gg); /* the |gg->data| blocks disappear, but |g->data| remains */ @* Terminal interaction. We've finished doing all the interesting things. Only one minor part of the program still remains to be written. @<Prompt for...@>= putchar('\n'); /* make a blank line for visual punctuation */ restart: /* if we try to avoid this label, the |break| command will be broken */ if (prompt_for_five("Starting",start)!=0) break; if (prompt_for_five(" Goal",goal)!=0) goto restart; @ @<Sub...@>= long prompt_for_five(s,p) char *s; /* string used in prompt message */ register char *p; /* where to put a string typed by the user */ {@+register char *q; /* current position to store characters */ register long c; /* current character of input */ while (1) { printf("%s word: ",s); fflush(stdout); /* make sure the user sees the prompt */ q=p; while (1) { c=getchar(); if (c==EOF) return -1; /* end-of-file */ if (echo) putchar(c); if (c=='\n') break; if (!islower(c)) q=p+5; else if (q<p+5) *q=c; q++; } if (q==p+5) return 0; /* got a good five-letter word */ if (q==p) return 1; /* got just \<return> */ printf("(Please type five lowercase letters and RETURN.)\n"); } } @* Index. Finally, here's a list that shows where the identifiers of this program are defined and used. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������miles_span.w����������������������������������������������������������������������������������������0000444�0001750�0001750�00000210710�10222173716�011522� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{MILES\_\,SPAN} \def\<#1>{$\langle${\rm#1}$\rangle$} \prerequisite{GB\_\,MILES} @* Minimum spanning trees. A classic paper by R. L. Graham and Pavol Hell about the history of @^Graham, Ronald Lewis@> @^Hell, Pavol@> algorithms to find the minimum-length spanning tree of a graph [{\sl Annals of the History of Computing \bf7} (1985), 43--57] describes three main approaches to that problem. Algorithm~1, ``two nearest fragments,'' repeatedly adds a shortest edge that joins two hitherto unconnected fragments of the graph; this algorithm was first published by J.~B. Kruskal in 1956. Algorithm~2, ``nearest @^Kruskal, Joseph Bernard@> neighbor,'' repeatedly adds a shortest edge that joins a particular fragment to a vertex not in that fragment; this algorithm was first published by V. Jarn\'{\i}k in 1930. Algorithm~3, ``all nearest @^Jarn{\'\i}k, Vojt\u ech@> fragments,'' repeatedly adds to each existing fragment the shortest edge that joins it to another fragment; this method, seemingly the most sophisticated in concept, also turns out to be the oldest, being first published by Otakar Bor{\accent23u}vka in 1926. @^Bor{\accent23u}vka, Otakar@> The present program contains simple implementations of all three approaches, in an attempt to make practical comparisons of how they behave on ``realistic'' data. One of the main goals of this program is to demonstrate a simple way to make machine-independent comparisons of programs written in \CEE/, by counting memory references or ``mems.'' In other words, this program is intended to be read, not just performed. The author believes that mem counting sheds considerable light on the problem of determining the relative efficiency of competing algorithms for practical problems. He hopes other researchers will enjoy rising to the challenge of devising algorithms that find minimum spanning trees in significantly fewer mem units than the algorithms presented here, on problems of the size considered here. Indeed, mem counting promises to be significant for combinatorial algorithms of all kinds. The standard graphs available in the Stanford GraphBase should make it possible to carry out a large number of machine-independent experiments concerning the practical efficiency of algorithms that have previously been studied only asymptotically. @ The graphs we will deal with are produced by the |miles| subroutine, found in the {\sc GB\_\,MILES} module. As explained there, |miles(n,north_weight,west_weight,pop_weight,0,max_degree,seed)| produces a graph of |n<=128| vertices based on the driving distances between North American cities. By default we take |n=100|, |north_weight=west_weight =pop_weight=0|, and |max_degree=10|; this gives billions of different sparse graphs, when different |seed| values are specified, since a different random number seed generally results in the selection of another one of the $\,128\,\choose100$ possible subgraphs. The default parameters can be changed by specifying options on the command line, at least in a \UNIX/ implementation, thereby obtaining a variety of special effects. For example, the value of |n| can be raised or lowered and/or the graph can be made more or less sparse. The user can bias the selection by ranking cities according to their population and/or position, if nonzero values are given to any of the parameters |north_weight|, |west_weight|, or |pop_weight|. Command-line options \.{-n}\<number>, \.{-N}\<number>, \.{-W}\<number>, \.{-P}\<number>, \.{-d}\<number>, and \.{-s}\<number> are used to specify non-default values of the respective quantities |n|, |north_weight|, |west_weight|, |pop_weight|, |max_degree|, and |seed|. If the user specifies a \.{-r} option, for example by saying `\.{miles\_span} \.{-r10}', this program will investigate the spanning trees of a series of, say, 10 graphs having consecutive |seed| values. (This option makes sense only if |north_weight=west_weight=pop_weight=0|, because |miles| chooses the top |n| cities by weight. The procedure rarely needs to use random numbers to break ties when the weights are nonzero, because cities rarely have exactly the same weight in that case.) The special command-line option \.{-g}$\langle\,$filename$\,\rangle$ overrides all others. It substitutes an external graph previously saved by |save_graph| for the graphs produced by |miles|. @^UNIX dependencies@> Here is the overall layout of this \CEE/ program: @p #include "gb_graph.h" /* the GraphBase data structures */ #include "gb_save.h" /* |restore_graph| */ #include "gb_miles.h" /* the |miles| routine */ @h@# @<Global variables@>@; @<Procedures to be declared early@>@; @<Priority queue subroutines@>@; @<Subroutines@>@; main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ {@+unsigned long n=100; /* the desired number of vertices */ unsigned long n_weight=0; /* the |north_weight| parameter */ unsigned long w_weight=0; /* the |west_weight| parameter */ unsigned long p_weight=0; /* the |pop_weight| parameter */ unsigned long d=10; /* the |max_degree| parameter */ long s=0; /* the random number seed */ unsigned long r=1; /* the number of repetitions */ char *file_name=NULL; /* external graph to be restored */ @<Scan the command-line options@>; while (r--) { if (file_name) g=restore_graph(file_name); else g=miles(n,n_weight,w_weight,p_weight,0L,d,s); if (g==NULL || g->n<=1) { fprintf(stderr,"Sorry, can't create the graph! (error code %ld)\n", panic_code); return -1; /* error code 0 means the graph is too small */ } @<Report the number of mems needed to compute a minimum spanning tree of |g| by various algorithms@>; gb_recycle(g); s++; /* increase the |seed| value */ } return 0; /* normal exit */ } @ @<Global...@>= Graph *g; /* the graph we will work on */ @ @<Scan the command-line options@>= while (--argc) { @^UNIX dependencies@> if (sscanf(argv[argc],"-n%lu",&n)==1) ; else if (sscanf(argv[argc],"-N%lu",&n_weight)==1) ; else if (sscanf(argv[argc],"-W%lu",&w_weight)==1) ; else if (sscanf(argv[argc],"-P%lu",&p_weight)==1) ; else if (sscanf(argv[argc],"-d%lu",&d)==1) ; else if (sscanf(argv[argc],"-r%lu",&r)==1) ; else if (sscanf(argv[argc],"-s%ld",&s)==1) ; else if (strcmp(argv[argc],"-v")==0) verbose=1; else if (strncmp(argv[argc],"-g",2)==0) file_name=argv[argc]+2; else { fprintf(stderr, "Usage: %s [-nN][-dN][-rN][-sN][-NN][-WN][-PN][-v][-gfoo]\n", argv[0]); return -2; } } if (file_name) r=1; @ We will try out four basic algorithms that have received prominent attention in the literature. Graham and Hell's Algorithm~1 is represented by the |krusk| procedure, which uses Kruskal's algorithm after the edges have been sorted by length with a radix sort. Their Algorithm~2 is represented by the |jar_pr| procedure, which incorporates a priority queue structure that we implement in two ways, either as a simple binary heap or as a Fibonacci heap. And their Algorithm~3 is represented by the |cher_tar_kar| procedure, which implements a method similar to Bor{\accent23u}vka's that was independently discovered by Cheriton and Tarjan and later simplified and refined by Karp and Tarjan. @^Cheriton, David Ross@> @^Tarjan, Robert Endre@> @^Karp, Richard Manning@> @d INFINITY (unsigned long)-1 /* value returned when there's no spanning tree */ @<Report the number...@>= printf("The graph %s has %ld edges,\n",g->id,g->m/2); sp_length=krusk(g); if (sp_length==INFINITY) printf(" and it isn't connected.\n"); else printf(" and its minimum spanning tree has length %ld.\n",sp_length); printf(" The Kruskal/radix-sort algorithm takes %ld mems;\n",mems); @<Execute |jar_pr(g)| with binary heaps as the priority queue algorithm@>; printf(" the Jarnik/Prim/binary-heap algorithm takes %ld mems;\n",mems); @<Allocate additional space needed by the more complex algorithms; or |goto done| if there isn't enough room@>; @<Execute |jar_pr(g)| with Fibonacci heaps as the priority queue algorithm@>; printf(" the Jarnik/Prim/Fibonacci-heap algorithm takes %ld mems;\n",mems); if (sp_length!=cher_tar_kar(g)) { if (gb_trouble_code) printf(" ...oops, I've run out of memory!\n"); else printf(" ...oops, I've got a bug, please fix fix fix\n"); return -3; } printf(" the Cheriton/Tarjan/Karp algorithm takes %ld mems.\n\n",mems); done:; @ @<Glob...@>= unsigned long sp_length; /* length of the minimum spanning tree */ @ When the |verbose| switch is nonzero, edges found by the various algorithms will call the |report| subroutine. @<Sub...@>= report(u,v,l) Vertex *u,*v; /* adjacent vertices in the minimum spanning tree */ long l; /* the length of the edge between them */ { printf(" %ld miles between %s and %s [%ld mems]\n", l,u->name,v->name,mems); } @*Strategies and ground rules. Let us say that a {\sl fragment\/} is any subtree of a minimum spanning tree. All three algorithms we implement make use of a basic principle first stated in full generality by R.~C. Prim in 1957: @^Prim, Robert Clay@> ``If a fragment~$F$ does not include all the vertices, and if $e$~is a shortest edge joining $F$ to a vertex not in~$F$, then $F\cup e$ is a fragment.'' To prove Prim's principle, let $T$ be a minimum spanning tree that contains $F$ but not~$e$. Adding $e$ to~$T$ creates a circuit containing some edge $e'\ne e$, where $e'$ runs from a vertex in~$F$ to a vertex not in~$F$. Deleting $e'$ from $T\cup e$ produces a spanning tree~$T'$ of total length no larger than the total length of~$T$. Hence $T'$ is a minimum spanning tree containing $F\cup e$, QED. @ The graphs produced by |miles| have special properties, and it is fair game to make use of those properties if we can. First, the length of each edge is a positive integer less than $2^{12}$. Second, the $k$th vertex $v_k$ of the graph is represented in \CEE/ programs by the pointer expression |g->vertices+k|. If weights have been assigned, these vertices will be in order by weight. For example, if |north_weight=1| but |west_weight=pop_weight=0|, vertex $v_0$ will be the most northerly city and vertex $v_{n-1}$ will be the most southerly. Third, the edges accessible from a vertex |v| appear in a linked list starting at |v->arcs|. An edge from |v| to $v_j$ will precede an edge from |v| to $v_k$ in this list if and only if $j>k$. Fourth, the vertices have coordinates |v->x_coord| and |v->y_coord| that are correlated with the length of edges between them: The Euclidean distance between the coordinates of two vertices tends to be small if and only if those vertices are connected by a relatively short edge. (This is only a tendency, not a certainty; for example, some cities around Chesapeake Bay are fairly close together as the crow flies, but not within easy driving range of each other.) Fifth, the edge lengths satisfy the triangle inequality: Whenever three edges form a cycle, the longest is no longer than the sum of the lengths of the two others. (It can be proved that the triangle inequality is of no use in finding minimum spanning trees; we mention it here only to exhibit yet another way in which the data produced by |miles| is known to be nonrandom.) Our implementation of Kruskal's algorithm will make use of the first property, and it also uses part of the third to avoid considering an edge more than once. We will not exploit the other properties, but a reader who wants to design algorithms that use fewer mems to find minimum spanning trees of these graphs is free to use any idea that helps. @ Speaking of mems, here are the simple \CEE/ instrumentation macros that we use to count memory references. The macros are called |o|, |oo|, |ooo|, and |oooo|; hence Jon Bentley has called this a ``little oh analysis.'' @^Bentley, Jon Louis@> Implementors who want to count mems are supposed to say, e.g., `|oo|,' just before an assignment statement or boolean expression that makes two references to memory. The \CEE/ preprocessor will convert this to a statement that increases |mems| by~2 as that statement or expression is evaluated. The semantics of \CEE/ tell us that the evaluation of an expression like `|a&&(o,a->len>10)|' will increment |mems| if and only if the pointer variable~|a| is non-null. Warning: The parentheses are very important in this example, because \CEE/'s operator |&&| (i.e., \.{\&\&}) has higher precedence than comma. Values of significant variables, like |a| in the previous example, can be assumed to be in ``registers,'' and no charge is made for arithmetic computations that involve only registers. But the total number of registers in an implementation must be finite and fixed, independent of the problem size. @^discussion of \\{mems}@> \CEE/ does not allow the |o| macros to appear in declarations, so we cannot take full advantage of \CEE/'s initialization mechanism when we are counting mems. But it's easy to initialize variables in separate statements after the declarations are done. @d o mems++ @d oo mems+=2 @d ooo mems+=3 @d oooo mems+=4 @<Glob...@>= long mems; /* the number of memory references counted */ @ Examples of these mem-counting conventions appear throughout the program that follows. Some people will undoubtedly ask why the insertion of macros by hand is being recommended here, when it would be possible to develop a fancy system that counts mems automatically. The author believes that it is best to rely on programmers to introduce |o| and |oo|, etc., by themselves, for several reasons. (1)~The macros can be inserted easily and quickly using a text editor. (2)~An implementation need not pay for mems that could be avoided by a suitable optimizing compiler or by making the \CEE/ program text slightly more complex; thus, authors can use their good judgment to keep programs more readable than if the code were overly hand-optimized. (3)~The programmer should be able to see exactly where mems are being charged, as an aid to bottleneck elimination. Occurrences of |o| and |oo| make this plain without messing up the program text. (4)~An implementation need not be charged for mems that merely provide diagnostic output, or mems that do redundant computations just to double-check the validity of ``proven'' assertions as a program is being tested. @^discussion of \\{mems}@> Computer architecture is converging rapidly these days to the design of machines in which the exact running time of a program depends on complicated interactions between pipelined circuitry and the dynamic properties of cache mapping in a memory hierarchy, not to mention the effects of compilers and operating systems. But a good approximation to running time is usually obtained if we assume that the amount of computation is proportional to the activity of the memory bus between registers and main memory. This approximation is likely to get even better in the future, as RISC computers get faster and faster in comparison to memory devices. Although the mem measure is far from perfect, it appears to be significantly less distorted than any other measurement that can be obtained without considerably more work. An implementation that is designed to use few mems will almost certainly be efficient on today's sequential computers, as well as on the sequential computers we can expect to be built in the foreseeable future. And the converse statement is even more true: An algorithm that runs fast will not consume many mems. Of course authors are expected to be reasonable and fair when they are competing for minimum-mem prizes. They must be ready to submit their programs to inspection by impartial judges. A good algorithm will not need to abuse the spirit of realistic mem-counting. Mems can be analyzed theoretically as well as empirically. This means we can attach constants to estimates of running time, instead of always resorting to $O$~notation. @*Kruskal's algorithm. The first algorithm we shall implement and instrument is the simplest: It considers the edges one by one in order of nondecreasing length, selecting each edge that does not form a cycle with previously selected edges. We know that the edge lengths are less than $2^{12}$, so we can sort them into order with two passes of a $2^6$-bucket radix sort. We will arrange to have them appear in the buckets as linked lists of |Arc| records; the two utility fields of an |Arc| will be called |from| and |klink|, respectively. @d from a.V /* an edge goes from vertex |a->from| to vertex |a->tip| */ @d klink b.A /* the next longer edge after |a| will be |a->klink| */ @<Put all the edges into |bucket[0]| through |bucket[63]|@>= o,n=g->n; for (l=0;l<64;l++) oo,aucket[l]=bucket[l]=NULL; for (o,v=g->vertices;v<g->vertices+n;v++) for (o,a=v->arcs;a&&(o,a->tip>v);o,a=a->next) { o,a->from=v; o,l=a->len&0x3f; /* length mod 64 */ oo,a->klink=aucket[l]; o,aucket[l]=a; } for (l=63;l>=0;l--) for (o,a=aucket[l];a;) {@+register long ll; register Arc *aa=a; o,a=a->klink; o,ll=aa->len>>6; /* length divided by 64 */ oo,aa->klink=bucket[ll]; o,bucket[ll]=aa; } @ @<Glob...@>= Arc *aucket[64], *bucket[64]; /* heads of linked lists of arcs */ @ Kruskal's algorithm now takes the following form. @<Sub...@>= unsigned long krusk(g) Graph *g; {@+@<Local variables for |krusk|@>@;@# mems=0; @<Put all the edges...@>; if (verbose) printf(" [%ld mems to sort the edges into buckets]\n",mems); @<Put all the vertices into components by themselves@>; for (l=0;l<64;l++) for (o,a=bucket[l];a;o,a=a->klink) { o,u=a->from; o,v=a->tip; @<If |u| and |v| are already in the same component, |continue|@>; if (verbose) report(a->from,a->tip,a->len); o,tot_len+=a->len; if (--components==1) return tot_len; @<Merge the components containing |u| and |v|@>; } return INFINITY; /* the graph wasn't connected */ } @ Lest we forget, we'd better declare all the local variables we've been using. @<Local variables for |krusk|@>= register Arc *a; /* current edge of interest */ register long l; /* current bucket of interest */ register Vertex *u,*v,*w; /* current vertices of interest */ unsigned long tot_len=0; /* total length of edges already chosen */ long n; /* the number of vertices */ long components; @ The remaining things that |krusk| needs to do are easily recognizable as an application of ``equivalence algorithms'' or ``union/find'' data structures. We will use a simple approach whose average running time on random graphs was shown to be linear by Knuth and Sch\"onhage @^Knuth, Donald Ervin@> @^Sch\"onhage, Arnold@> in {\sl Theoretical Computer Science\/ \bf 6} (1978), 281--315. The vertices of each component (that is, of each connected fragment defined by the edges selected so far) will be linked circularly by |clink| pointers. Each vertex also has a |comp| field that points to a unique vertex representing its component. Each component representative also has a |csize| field that tells how many vertices are in the component. @d clink z.V /* pointer to another vertex in the same component */ @d comp y.V /* pointer to component representative */ @d csize x.I /* size of the component (maintained only for representatives) */ @<If |u| and |v| are already in the same component, |continue|@>= if (oo,u->comp==v->comp) continue; @ We don't need to charge any mems for fetching |g->vertices|, because |krusk| has already referred to it. @^discussion of \\{mems}@> @<Put all the vertices...@>= for (v=g->vertices;v<g->vertices+n;v++) { oo,v->clink=v->comp=v; o,v->csize=1; } components=n; @ The operation of merging two components together requires us to change two |clink| pointers, one |csize| field, and the |comp| fields in each vertex of the smaller component. Here we charge two mems for the first |if| test, since |u->csize| and |v->csize| are being fetched from memory. Then we charge only one mem when |u->csize| is being updated, since the values being added together have already been fetched. True, the compiler has to be smart to realize that it's safe to add the fetched values |u->csize+v->csize| even though |u| and |v| might have been swapped in the meantime; but we are assuming that the compiler is extremely clever. (Otherwise we would have to clutter up our program every time we don't trust the compiler. After all, programs that count mems are intended primarily to be read. They aren't intended for production jobs.) % Prim-arily? @^discussion of \\{mems}@> @<Merge the components containing |u| and |v|@>= u=u->comp; /* |u->comp| has already been fetched from memory */ v=v->comp; /* ditto for |v->comp| */ if (oo,u->csize<v->csize) { w=u;@+u=v;@+v=w; } /* now |v|'s component is smaller than |u|'s (or equally small) */ o,u->csize+=v->csize; o,w=v->clink; oo,v->clink=u->clink; o,u->clink=w; for (;;o,w=w->clink) { o,w->comp=u; if (w==v) break; } @* Jarn{\'\i}k and Prim's algorithm. A second approach to minimum spanning trees is also pretty simple, except for one technicality: We want to write it in a sufficiently general manner that different priority queue algorithms can be plugged in. The basic idea is to choose an arbitrary vertex $v_0$ and connect it to its nearest neighbor~$v_1$, then to connect that fragment to its nearest neighbor~$v_2$, and so on. A priority queue holds all vertices that are adjacent to but not already in the current fragment; the key value stored with each vertex is its distance to the current fragment. We want the priority queue data structure to support the four operations |init_queue(d)|, |enqueue(v,d)|, |requeue(v,d)|, and |del_min()|, described in the {\sc GB\_\,DIJK} module. Dijkstra's algorithm for shortest paths, described there, is remarkably similar to Jarn{\'\i}k and Prim's algorithm for minimum spanning trees; in fact, Dijkstra discovered the latter algorithm independently, at the @^Dijkstra, Edsger Wybe@> same time as he came up with his procedure for shortest paths. As in {\sc GB\_\,DIJK}, we define pointers to priority queue subroutines so that the queueing mechanism can be varied. @d dist z.I /* this is the key field for vertices in the priority queue */ @d backlink y.V /* this vertex is the stated |dist| away */ @<Glob...@>= void @[@] (*init_queue)(); /* create an empty priority queue */ void @[@] (*enqueue)(); /* insert a new element in the priority queue */ void @[@] (*requeue)(); /* decrease the key of an element in the queue */ Vertex *(*del_min)(); /* remove an element with smallest key */ @ The vertices in this algorithm are initially ``unseen''; they become ``seen'' when they enter the priority queue, and finally ``known'' when they leave it and enter the current fragment. We will put a special constant in the |backlink| field of known vertices. A vertex will be unseen if and only if its |backlink| is~|NULL|. @d KNOWN (Vertex*)1 /* special |backlink| to mark known vertices */ @<Sub...@>= unsigned long jar_pr(g) Graph *g; {@+register Vertex *t; /* vertex that is just becoming known */ long fragment_size; /* number of vertices in the tree so far */ unsigned long tot_len=0; /* sum of edge lengths in the tree so far */ mems=0; @<Make |t=g->vertices| the only vertex seen; also make it known@>; while (fragment_size<g->n) { @<Put all unseen vertices adjacent to |t| into the queue, and update the distances of the other vertices adjacent to~|t|@>; t=(*del_min)(); if (t==NULL) return INFINITY; /* the graph is disconnected */ if (verbose) report(t->backlink,t,t->dist); o,tot_len+=t->dist; o,t->backlink=KNOWN; fragment_size++; } return tot_len; } @ Notice that we don't charge any mems for the subroutine call to |init_queue|, except for mems counted in the subroutine itself. What should we charge in general for subroutine linkage when we are counting mems? The parameters to subroutines generally go into registers, and registers are ``free''; also, a compiler can often choose to implement a procedure in line, thereby reducing the overhead to zero. Hence, the recommended method for charging mems with respect to subroutines is: Charge nothing if the subroutine is not recursive; otherwise charge twice the number of things that need to be saved on a runtime stack. (The return address is one of the things that needs to be saved.) @^discussion of \\{mems}@> @<Make |t=g->vertices| the only vertex seen; also make it known@>= for (oo,t=g->vertices+g->n-1;t>g->vertices;t--) o,t->backlink=NULL; o,t->backlink=KNOWN; fragment_size=1; (*init_queue)(0L); /* make the priority queue empty */ @ @<Put all unseen vertices adjacent to |t| into the queue, and update the distances of the other vertices adjacent to~|t|@>= {@+register Arc *a; /* an arc leading from |t| */ for (o,a=t->arcs; a; o,a=a->next) { register Vertex *v; /* a vertex adjacent to |t| */ o,v=a->tip; if (o,v->backlink) { /* |v| has already been seen */ if (v->backlink>KNOWN) { if (oo,a->len<v->dist) { o,v->backlink=t; (*requeue)(v,a->len); /* we found a better way to get there */ } } }@+else { /* |v| hasn't been seen before */ o,v->backlink=t; o,(*enqueue)(v,a->len); } } } @*Binary heaps. To complete the |jar_pr| routine, we need to fill in the four priority queue functions. Jarn{\'\i}k wrote his original paper before computers were known; Prim and Dijkstra wrote theirs before efficient priority queue algorithms were known. Their original algorithms therefore took $\Theta(n^2)$ steps. Kerschenbaum and Van Slyke pointed out in 1972 that binary heaps could @^Kerschenbaum, A.@> @^Van Slyke, Richard Maurice@> do better. A simplified version of binary heaps (invented by Williams @^Williams, John William Joseph@> in 1964) is presented here. A binary heap is an array of $n$ elements, and we need space for it. Fortunately the space is already there; we can use utility field |u| in each of the vertex records of the graph. Moreover, if |heap_elt(i)| points to vertex~|v|, we will arrange things so that |v->heap_index=i|. @d heap_elt(i) (gv+i)->u.V /* the |i|th vertex of the heap; |gv=g->vertices| */ @d heap_index v.I /* the |v| utility field says where a vertex is in the heap */ @<Glob...@>= Vertex *gv; /* |g->vertices|, the base of the heap array */ long hsize; /* the number of elements currently in the heap */ @ To initialize the heap, we need only initialize two ``registers'' to known values, so we don't have to charge any mems at all. (In a production implementation, this code would appear in-line as part of the spanning tree algorithm.) @^discussion of \\{mems}@> Important Note: This routine refers to the global variable |g|, which is set in |main| (not in |jar_pr|). Suitable changes need to be made if these binary heap routines are used in other programs. @<Priority queue subroutines@>= void init_heap(d) /* makes the heap empty */ long d; { gv=g->vertices; hsize=0; } @ The key invariant property that makes heaps work is $$\hbox{|heap_elt(k/2)->dist<=heap_elt(k)->dist|, \qquad for |1<k<=hsize|.}$$ (A reader who has not seen heap ordering before should stop at this point and study the beautiful consequences of this innocuously simple set of inequalities.) The enqueueing operation turns out to be quite simple: @<Priority queue subroutines@>= void enq_heap(v,d) Vertex *v; /* vertex that is entering the queue */ long d; /* its key (aka |dist|) */ {@+register unsigned long k; /* position of a ``hole'' in the heap */ register unsigned long j; /* the parent of that position */ register Vertex *u; /* |heap_elt(j)| */ o,v->dist=d; k=++hsize; j=k>>1; /* |k/2| */ while (j>0 && (oo,(u=heap_elt(j))->dist>d)) { o,heap_elt(k)=u; /* the hole moves to parent position */ o,u->heap_index=k; k=j; j=k>>1; } o,heap_elt(k)=v; o,v->heap_index=k; } @ And in fact, the general requeueing operation is almost identical to enqueueing. This operation is popularly called ``siftup,'' because the vertex whose key is being reduced may displace its ancestors higher in the heap. We could have implemented enqueueing by first placing the new element at the end of the heap, then requeueing it; that would have cost at most a couple mems more. @<Priority queue subroutines@>= void req_heap(v,d) Vertex *v; /* vertex whose key is being reduced */ long d; /* its new |dist| */ {@+register unsigned long k; /* position of a ``hole'' in the heap */ register unsigned long j; /* the parent of that position */ register Vertex *u; /* |heap_elt(j)| */ o,v->dist=d; o,k=v->heap_index; /* now |heap_elt(k)=v| */ j=k>>1; /* |k/2| */ if (j>0 && (oo,(u=heap_elt(j))->dist>d)) { /* change is needed */ do@+{ o,heap_elt(k)=u; /* the hole moves to parent position */ o,u->heap_index=k; k=j; j=k>>1; /* |k/2| */ }@+while (j>0 && (oo,(u=heap_elt(j))->dist>d)); o,heap_elt(k)=v; o,v->heap_index=k; } } @ Finally, the procedure for removing the vertex with smallest key is only a bit more difficult. The vertex to be removed is always |heap_elt(1)|. After we delete it, we ``sift down'' |heap_elt(hsize)|, until the basic heap inequalities hold once again. At a crucial point in this process, we have |j->dist<u->dist|. We cannot then have |j=hsize+1|, because the previous steps have made |(hsize+1)->dist=u->dist=d|. @<Prior...@>= Vertex *del_heap() {@+Vertex *v; /* vertex to return */ register Vertex *u; /* vertex being sifted down */ register unsigned long k; /* hole in the heap */ register unsigned long j; /* child of that hole */ register long d; /* |u->dist|, the vertex of the vertex being sifted */ if (hsize==0) return NULL; o,v=heap_elt(1); o,u=heap_elt(hsize--); o,d=u->dist; k=1; j=2; while (j<=hsize) { if (oooo,heap_elt(j)->dist>heap_elt(j+1)->dist) j++; if (heap_elt(j)->dist>=d) break; o,heap_elt(k)=heap_elt(j); /* NB: we cannot have |j>hsize|, see above */ o,heap_elt(k)->heap_index=k; k=j; /* the hole moves to child position */ j=k<<1; /* |2k| */ } o,heap_elt(k)=u; o,u->heap_index=k; return v; } @ OK, here's how we plug binary heaps into Jarn{\'\i}k/Prim. @<Execute |jar_pr(g)| with binary heaps as the priority queue algorithm@>= init_queue=init_heap; enqueue=enq_heap; requeue=req_heap; del_min=del_heap; if (sp_length!=jar_pr(g)) { printf(" ...oops, I've got a bug, please fix fix fix\n"); return -4; } @*Fibonacci heaps. The running time of Jarn{\'\i}k/Prim with binary heaps, when the algorithm is applied to a connected graph with $n$ vertices and $m$ edges, is $O(m\log n)$, because the total number of operations is $O(m+n)=O(m)$ and each heap operation takes at most $O(\log n)$ time. Fibonacci heaps were invented by Fredman and Tarjan in 1984, in order @^Fibonacci, Leonardo, heaps@> @^Fredman, Michael Lawrence@> @^Tarjan, Robert Endre@> to do better than this. The Jarn{\'\i}k/Prim algorithm does $O(n)$ enqueueing operations, $O(n)$ delete-min operations, and $O(m)$ requeueing operations; so Fredman and Tarjan designed a data structure that would support requeueing in ``constant amortized time.'' In other words, Fibonacci heaps allow us to do $m$ requeueing operations with a total cost of~$O(m)$, even though some of the individual requeueings might take longer. The resulting asymptotic running time is then $O(m+n\log n)$. (This turns out to be optimum within a constant factor, when the same technique is applied to Dijkstra's algorithm for shortest paths. But for minimum spanning trees the Fibonacci method is not always optimum; for example, if $m\approx n\sqrt{\mathstrut\log n}$, the algorithm of Cheriton and Tarjan has slightly better asymptotic behavior, $O(m\log\log n)$.) Fibonacci heaps are more complex than binary heaps, so we can expect that overhead costs will make them non-competitive unless $m$ and $n$ are quite large. Furthermore, it is not clear that the running time with simple binary heaps will behave as $m\log n$ on realistic data, because $O(m\log n)$ is a worst-case estimate based on rather pessimistic assumptions. (For example, requeueing might rarely require many iterations of the siftup loop.) But it will be instructive to implement Fibonacci heaps as best we can, just to see how good they look in actual practice. Let us say that the {\sl rank\/} of a node in a forest is the number of children it has. A Fibonacci heap is an unordered forest of trees in which the key of each node is less than or equal to the key of each child of that node, and in which the following further condition, called property~F, also holds: The ranks $\{r_1,r_2,\ldots,r_k\}$ of the children of every node of rank~$k$, when put into nondecreasing order $r_1\le r_2\le\cdots\le r_k$, satisfy $r_j\ge j-2$ for all~$j$. As a consequence of property F, we can prove by induction that every node of rank~$k$ has at least $F_{k+2}$ descendants (including itself). Therefore, for example, we cannot have a node of rank $\ge30$ unless the total size of the forest is at least $F_{32}=2{,}178{,}309$. We cannot have a node of rank $\ge46$ unless the total size of the forest exceeds~$2^{32}$. @ We will represent a Fibonacci heap with a rather elaborate data structure, in order to guarantee the efficiency of all the necessary operations. Each node will have four pointers: |parent|, the node's parent (or |NULL| if the node is a root); |child|, one of the node's children (or undefined if the node has no children); |lsib| and |rsib|, the node's left and right siblings. The children of each node, and the roots of the forest, are doubly linked by |lsib| and |rsib| in circular lists; the nodes in these lists can appear in any convenient order, and the |child| pointer can point to any child. Besides the four pointers, there is a \\{rank} field, which tells how many children exist, and a \\{tag} field, which is either 0 or~1. Suppose a node has children of ranks $\{r_1,r_2,\ldots,r_k\}$, where $r_1\le r_2\le\cdots\le r_k$. We know that $r_j\ge j-2$ for all~$j$; we say that the node has $l$ {\sl critical\/} children if there are $l$ cases of equality, where $r_j=j-2$. Our implementation will guarantee that any node with $l$ critical children will have at least $l$ tagged children of the corresponding ranks. For example, suppose a node has seven children, of respective ranks $\{1,1,1,2,4,4,6\}$. Then it has three critical children, because $r_3=1$, $r_4=2$, and $r_6=4$. In our implementation, at least one of the children of rank~1 will have $\\{tag}=1$, and so will the child of rank~2; so will one of the children of rank~4. There is an external pointer called |F_heap|, which indicates a node whose key is smallest. (If the heap is empty, |F_heap| is~|NULL|.) @<Prior...@>= void init_F_heap(d) long d; {@+F_heap=NULL;@+} @ @<Glob...@>= Vertex *F_heap; /* pointer to the ring of root nodes */ @ We can save a bit of space and time by combining the \\{rank} and \\{tag} fields into a single |rank_tag| field, which contains $\\{rank}*2+\\{tag}$. Vertices in GraphBase graphs have six utility fields. That's just enough for |parent|, |child|, |lsib|, |rsib|, |rank_tag|, and the key field |dist|. But unfortunately we also need the |backlink| field, so we are over the limit. That's not really so bad, however; we can set up another array of $n$ records, and point to it. The extra running time needed for indirect pointing does not have to be charged to mems, because a production system involving Fibonacci heaps would simply redefine |Vertex| records to have seven utility fields instead of six. In this way we can simulate the behavior of larger records without changing the basic GraphBase conventions. @^discussion of \\{mems}@> We will want an |Arc| record for each vertex in our next algorithm, so we might as well allocate storage for it now even though Fibonacci heaps need only two of the five fields. @d newarc u.A /* |v->newarc| points to an |Arc| record associated with |v| */ @d parent newarc->tip @d child newarc->a.V @d lsib v.V @d rsib w.V @d rank_tag x.I @<Allocate additional space needed by the more complex algorithms...@>= {@+register Arc *aa; register Vertex *uu; aa=gb_typed_alloc(g->n,Arc,g->aux_data); if (aa==NULL) { printf(" and there isn't enough space to try the other methods.\n\n"); goto done; } for (uu=g->vertices;uu<g->vertices+g->n;uu++,aa++) uu->newarc=aa; } @ The {\sl potential energy\/} of a Fibonacci heap, as we are representing it, is defined to be the number of trees in the forest plus twice the total number of tagged children. When we operate on a heap, we will store potential energy to be used up later; then it will be possible to do the later operations with only a small incremental cost to the running time. (Potential energy is just a way to prove that the amortized cost is small; it does not appear explicitly in our implementation. It simply explains why the number of mems we compute will always be $O(m+n\log n)$.) Enqueueing is easy: We simply insert the new element as a new tree in the forest. This costs a constant amount of time, including the cost of one new unit of potential energy for the new tree. We can assume that |F_heap->dist| appears in a register, so we need not charge a mem to fetch~it. @<Prior...@>= void enq_F_heap(v,d) Vertex *v; /* vertex that is entering the queue */ long d; /* its key (aka |dist|) */ { o,v->dist=d; o,v->parent=NULL; o,v->rank_tag=0; /* |v->child| need not be set */ if (F_heap==NULL) { oo,F_heap=v->lsib=v->rsib=v; }@+else {@+register Vertex *u; o,u=F_heap->lsib; o,v->lsib=u; o,v->rsib=F_heap; oo,F_heap->lsib=u->rsib=v; if (F_heap->dist>d) F_heap=v; } } @ Requeueing is of medium difficulty. If the key is being decreased in a root node, or if the decrease doesn't make the key less than the key of its parent, no links need to change (except possibly |F_heap| itself). Otherwise we detach the node and its descendants from its present family and put this former subtree into the forest as a new tree. (One unit of potential energy must be stored with it.) The rank of the former parent, |p|, decreases by~1. If |p| is a root, we're done. Otherwise if |p| was not tagged, we tag it (and pay for two additional units of energy). Property~F still holds, because an untagged node can always admit a decrease in rank. If |p| was tagged, however, we detach |p| and its remaining descendants, making it another new tree of the forest, with |p| no longer tagged. Removing the tag releases enough stored energy to pay for the extra work of moving~|p|. Then we must decrease the rank of |p|'s parent, and so on, until finally we get to a root or to an untagged node. The total net cost is at most three units of energy plus the cost of relinking the original node, so it is $O(1)$. We needn't clear the tag fields of root nodes, because we never look at them. @<Prior...@>= void req_F_heap(v,d) Vertex *v; /* vertex whose key is being reduced */ long d; /* its new |dist| */ {@+register Vertex *p,*pp; /* parent and grandparent of |v| */ register Vertex *u,*w; /* other vertices being modified */ register long r; /* twice the rank plus the tag */ o,v->dist=d; o,p=v->parent; if (p==NULL) { if (F_heap->dist>d) F_heap=v; }@+else if (o,p->dist>d) while(1) { o,r=p->rank_tag; if (r>=4) /* |v| is not an only child */ @<Remove |v| from its family@>; @<Insert |v| into the forest@>; o,pp=p->parent; if (pp==NULL) { /* the parent of |v| is a root */ o,p->rank_tag=r-2;@+break; } if ((r&1)==0) { /* the parent of |v| is untagged */ o,p->rank_tag=r-1;@+break; /* now it's tagged */ }@+else o,p->rank_tag=r-2; /* tagged parent will become a root */ v=p;@+p=pp; } } @ @<Remove |v| from its family@>= { o,u=v->lsib; o,w=v->rsib; o,u->rsib=w; o,w->lsib=u; if (o,p->child==v) o,p->child=w; } @ @<Insert |v| into the forest@>= o,v->parent=NULL; o,u=F_heap->lsib; o,v->lsib=u; o,v->rsib=F_heap; oo,F_heap->lsib=u->rsib=v; if (F_heap->dist>d) F_heap=v; /* this can happen only with the original |v| */ @ The |del_min| operation is even more interesting; this, in fact, is where most of the action lies. We know that |F_heap| points to the vertex~$v$ we will be deleting. That's nice, but we need to figure out the new value of |F_heap|. So we have to look at all the children of~$v$ and at all the root nodes in the forest. We have stored up enough potential energy to do that, but we can reclaim the potential only if we rebuild the Fibonacci heap so that the rebuilt version contains relatively few trees. The solution is to make sure that the new heap has at most one root of each rank. Whenever we have two tree roots of equal rank, we can make one the child of the other, thus reducing the number of trees by~1. (The new child does not violate Property~F, nor is it critical, so we can mark it untagged.) The largest rank is always $O(\log n)$, if there are $n$ nodes altogether, and we can afford to pay $\log n$ units of time for the work that isn't reclaimed from potential energy. An array of pointers to roots of known rank is used to help control this part of the process. @<Glob...@>= Vertex *new_roots[46]; /* big enough for queues of size $2^{32}$ */ @ @<Prio...@>= Vertex *del_F_heap() {@+Vertex *final_v=F_heap; /* the node to return */ register Vertex *t,*u,*v,*w; /* registers for manipulation of links */ register long h=-1; /* the highest rank present in |new_roots| */ register long r; /* rank of current tree */ if (F_heap) { if (o,F_heap->rank_tag<2) o,v=F_heap->rsib; else { o,w=F_heap->child; o,v=w->rsib; oo,w->rsib=F_heap->rsib; /* link children of deleted node into the list */ for (w=v;w!=F_heap->rsib;o,w=w->rsib) o,w->parent=NULL; } while (v!=F_heap) { o,w=v->rsib; @<Put the tree rooted at |v| into the |new_roots| forest@>; v=w; } @<Rebuild |F_heap| from |new_roots|@>; } return final_v; } @ The work we do in this step is paid for by the unit of potential energy being freed as |v| leaves the old forest, except for the work of increasing~|h|; we charge the latter to the $O(\log n)$ cost of building |new_roots|. @<Put the tree rooted at |v| into the |new_roots| forest@>= o,r=v->rank_tag>>1; while (1) { if (h<r) { do@+{ h++; o,new_roots[h]=(h==r?v:NULL); }@+while (h<r); break; } if (o,new_roots[r]==NULL) { o,new_roots[r]=v; break; } u=new_roots[r]; o,new_roots[r]=NULL; if (oo,u->dist<v->dist) { o,v->rank_tag=r<<1; /* |v| is not critical and needn't be tagged */ t=u;@+u=v;@+v=t; } @<Make |u| a child of |v|@>; r++; } o,v->rank_tag=r<<1; /* every root in |new_roots| is untagged */ @ When we get to this step, |u| and |v| both have rank |r|, and |u->dist>=v->dist|; |u| is untagged. @<Make |u| a child of |v|@>= if (r==0) { o,v->child=u; oo,u->lsib=u->rsib=u; }@+else { o,t=v->child; oo,u->rsib=t->rsib; o,u->lsib=t; oo,u->rsib->lsib=t->rsib=u; } o,u->parent=v; @ And now we can breathe easy, because the last step is trivial. @<Rebuild |F_heap| from |new_roots|@>= if (h<0) F_heap=NULL; else {@+long d; /* smallest key value seen so far */ o,u=v=new_roots[h]; /* |u| and |v| will point to beginning and end of list, respectively */ o,d=u->dist; F_heap=u; for (h--;h>=0;h--) if (o,new_roots[h]) { w=new_roots[h]; o,w->lsib=v; o,v->rsib=w; if (o,w->dist<d) { F_heap=w; d=w->dist; } v=w; } o,v->rsib=u; o,u->lsib=v; } @ @<Execute |jar_pr(g)| with Fibonacci heaps...@>= init_queue=init_F_heap; enqueue=enq_F_heap; requeue=req_F_heap; del_min=del_F_heap; if (sp_length!=jar_pr(g)) { printf(" ...oops, I've got a bug, please fix fix fix\n"); return -5; } @*Binomial queues. Jean Vuillemin's ``binomial queue'' structures [{\sl CACM\/ \bf21} (1978), @^Vuillemin, Jean Etienne@> 309--314] provide yet another appealing way to maintain priority queues. A binomial queue is a forest of trees with keys ordered as in Fibonacci heaps, satisfying two conditions that are considerably stronger than the Fibonacci heap property: Each node of rank~$k$ has children of respective ranks $\{0,1,\ldots,k-1\}$; and each root of the forest has a different rank. It follows that each node of rank~$k$ has exactly $2^k$ descendants (including itself), and that a binomial queue of $n$ elements has exactly as many trees as the number $n$ has 1's in binary notation. We could plug binomial queues into the Jarn{\'\i}k/Prim algorithm, but they don't offer advantages over the heap methods already considered because they don't support the requeueing operation as nicely. Binomial queues do, however, permit efficient merging---the operation of combining two priority queues into one---and they achieve this without as much space overhead as Fibonacci heaps. In fact, we can implement binomial queues with only two pointers per node, namely a pointer to the largest child and another to the next sibling. This means we have just enough space in the utility fields of GraphBase |Arc| records to link the arcs that extend out of a spanning tree fragment. The algorithm of Cheriton, Tarjan, and Karp, which we will consider soon, maintains priority queues of arcs, not vertices; and it requires the operation of merging, not requeueing. Therefore binomial queues are well suited to it, and we will prepare ourselves for that algorithm by implementing basic binomial queue procedures. Incidentally, if you wonder why Vuillemin called his structure a binomial queue, it's because the trees of $2^k$ elements have many pleasant combinatorial properties, among which is the fact that the number of elements on level~$l$ is the binomial coefficient~$k\choose l$. The backtrack tree for subsets of a $k$-set has the same structure. A picture of a binomial-queue tree with $k=5$, drawn by @^Knuth, Nancy Jill Carter@> Jill~C. Knuth, appears as the frontispiece of {\sl The Art of Computer Programming}, facing page~1 of Volume~1. @d qchild a.A /* pointer to the arc for largest child of an arc */ @d qsib b.A /* pointer to next larger sibling, or from largest to smallest */ @ A special header node is used at the head of a binomial queue, to represent the queue itself. The |qsib| field of this node points to the smallest root node in the forest. (``Smallest'' means smallest in rank, not in key value.) The header also contains a |qcount| field, which takes the place of |qchild|; the |qcount| is the total number of nodes, so its binary representation characterizes the sizes of the trees accessible from |qsib|. For example, suppose a queue with header node |h| contains five elements $\{a,b,c,d,e\}$ whose keys happen to be ordered alphabetically. The first tree might be the single node~$c$; the other tree might be rooted at~$a$, with children $e$ and~$b$. Then we have $$\vbox{\halign{#\hfil&\qquad#\hfil\cr |h->qcount=5|,&|h->qsib=c|;\cr |c->qsib=a|;\cr |a->qchild=b|;\cr |b->qchild=d|,&|b->qsib=e|;\cr |e->qsib=b|.\cr}}$$ The other fields |c->qchild|, |a->qsib|, |e->qchild|, |d->qsib|, and |d->qchild| are undefined. We can save time by not loading or storing the undefined fields, which make up about 3/8 of the structure. An empty binomial queue would have |h->qcount=0| and |h->qsib| undefined. Like Fibonacci heaps, binomial queues store potential energy: The number of energy units present is simply the number of trees in the forest. @d qcount a.I /* this field takes the place of |qchild| in header nodes */ @ Most of the operations we want to do with binomial queues rely on the following basic subroutine, which merges a forest of |m| nodes starting at |q| with a forest of |mm| nodes starting at |qq|, putting a pointer to the resulting forest of |m+mm| nodes into |h->qsib|. The amortized running time is $O(\log m)$, independent of |mm|. The |len| field, not |dist|, is the key field for this queue, because our nodes in this case are arcs instead of vertices. @<Prio...@>= qunite(m,q,mm,qq,h) register long m,mm; /* number of nodes in the forests */ register Arc *q,*qq; /* binomial trees in the forests, linked by |qsib| */ Arc *h; /* |h->qsib| will get the result */ {@+register Arc *p; /* tail of the list built so far */ register long k=1; /* size of trees currently being processed */ p=h; while (m) { if ((m&k)==0) { if (mm&k) { /* |qq| goes into the merged list */ o,p->qsib=qq;@+p=qq;@+mm-=k; if (mm) o,qq=qq->qsib; } }@+else if ((mm&k)==0) { /* |q| goes into the merged list */ o,p->qsib=q;@+p=q;@+m-=k; if (m) o,q=q->qsib; }@+else @<Combine |q| and |qq| into a ``carry'' tree, and continue merging until the carry no longer propagates@>; k<<=1; } if (mm) o,p->qsib=qq; } @ As we have seen in Fibonacci heaps, two heap-ordered trees can be combined by simply attaching one as a new child of the other. This operation preserves binomial trees. (In fact, if we use Fibonacci heaps without ever doing a requeue operation, the forests that appear after every |del_min| are binomial queues.) The number of trees decreases by~1, so we have a unit of potential energy to pay for this computation. @<Combine |q| and |qq| into a ``carry'' tree, and continue merging until the carry no longer propagates@>= {@+register Arc *c; /* the ``carry,'' a tree of size |2k| */ register long key; /* |c->len| */ register Arc *r,*rr; /* remainders of the input lists */ m-=k;@+if (m) o,r=q->qsib; mm-=k;@+if (mm) o,rr=qq->qsib; @<Set |c| to the combination of |q| and |qq|@>; k<<=1;@+q=r;@+qq=rr; while ((m|mm)&k) { if ((m&k)==0) @<Merge |qq| into |c| and advance |qq|@>@; else { @<Merge |q| into |c| and advance |q|@>; if (mm&k) { o,p->qsib=qq;@+p=qq;@+mm-=k; if (mm) o,qq=qq->qsib; } } k<<=1; } o,p->qsib=c;@+p=c; } @ @<Set |c| to the combination of |q| and |qq|@>= if (oo,q->len<qq->len) { c=q,key=q->len; q=qq; }@+else c=qq,key=qq->len; if (k==1) o,c->qchild=q; else { o,qq=c->qchild; o,c->qchild=q; if (k==2) o,q->qsib=qq; else oo,q->qsib=qq->qsib; o,qq->qsib=q; } @ At this point, |k>1|. @<Merge |q| into |c| and advance |q|@>= { m-=k;@+if (m) o,r=q->qsib; if (o,q->len<key) { rr=c;@+c=q;@+key=q->len;@+q=rr; } o,rr=c->qchild; o,c->qchild=q; if (k==2) o,q->qsib=rr; else oo,q->qsib=rr->qsib; o,rr->qsib=q; q=r; } @ @<Merge |qq| into |c| and advance |qq|@>= { mm-=k;@+if (mm) o,rr=qq->qsib; if (o,qq->len<key) { r=c;@+c=qq;@+key=qq->len;@+qq=r; } o,r=c->qchild; o,c->qchild=qq; if (k==2) o,qq->qsib=r; else oo,qq->qsib=r->qsib; o,r->qsib=qq; qq=rr; } @ OK, now the hard work is done and we can reap the benefits of the basic |qunite| routine. One easy application enqueues a new arc in $O(1)$ amortized time. @<Prio...@>= qenque(h,a) Arc *h; /* header of a binomial queue */ Arc *a; /* new element for that queue */ {@+long m; o,m=h->qcount; o,h->qcount=m+1; if (m==0) o,h->qsib=a; else o,qunite(1L,a,m,h->qsib,h); } @ Here, similarly, is a routine that merges one binomial queue into another. The amortized running time is proportional to the logarithm of the number of nodes in the smaller queue. @<Prio...@>= qmerge(h,hh) Arc *h; /* header of binomial queue that will receive the result */ Arc *hh; /* header of binomial queue that will be absorbed */ {@+long m,mm; o,mm=hh->qcount; if (mm) { o,m=h->qcount; o,h->qcount=m+mm; if (m>=mm) oo,qunite(mm,hh->qsib,m,h->qsib,h); else if (m==0) oo,h->qsib=hh->qsib; else oo,qunite(m,h->qsib,mm,hh->qsib,h); } } @ The other important operation is, of course, deletion of a node with the smallest key. The amortized running time is proportional to the logarithm of the queue size. @<Prio...@>= Arc *qdel_min(h) Arc *h; /* header of binomial queue */ {@+register Arc *p,*pp; /* current node and its predecessor */ register Arc *q,*qq; /* current minimum node and its predecessor */ register long key; /* |q->len|, the smallest key known so far */ long m; /* number of nodes in the queue */ long k; /* number of nodes in tree |q| */ register long mm; /* number of nodes not yet considered */ o,m=h->qcount; if (m==0) return NULL; o,h->qcount=m-1; @<Find and remove a tree whose root |q| has the smallest key@>; if (k>2) { if (k+k<=m) oo,qunite(k-1,q->qchild->qsib,m-k,h->qsib,h); else oo,qunite(m-k,h->qsib,k-1,q->qchild->qsib,h); }@+else if (k==2) o,qunite(1L,q->qchild,m-k,h->qsib,h); return q; } @ If the tree with smallest key is the largest in the forest, we don't have to change any links to remove it, because our binomial queue algorithms never look at the last |qsib| pointer. We use a well-known binary number trick: |m&(m-1)| is the same as |m|, except that the least significant 1~bit is deleted. @<Find and remove...@>= mm=m&(m-1); o,q=h->qsib; k=m-mm; if (mm) { /* there's more than one tree */ p=q;@+qq=h; o,key=q->len; do@+{@+long t=mm&(mm-1); pp=p;@+o,p=p->qsib; if (o,p->len<=key) { q=p;@+qq=pp;@+k=mm-t;@+key=p->len; } mm=t; }@+while (mm); if (k+k<=m) oo,qq->qsib=q->qsib; /* remove the tree rooted at |q| */ } @ To complete our implementation, here is an algorithm that traverses a binomial queue, ``visiting'' each node exactly once, destroying the queue as it goes. The total number of mems required is about |1.75m|. @<Prio...@>= qtraverse(h,visit) Arc *h; /* head of binomial queue to be unraveled */ void @[@] (*visit)(); /* procedure to be invoked on each node */ {@+register long m; /* the number of nodes remaining */ register Arc *p,*q,*r; /* current position and neighboring positions */ o,m=h->qcount; p=h; while (m) { o,p=p->qsib; (*visit)(p); if (m&1) m--; else { o,q=p->qchild; if (m&2) (*visit)(q); else { o,r=q->qsib; if (m&(m-1)) oo,q->qsib=p->qsib; (*visit)(r); p=r; } m-=2; } } } @* Cheriton, Tarjan, and Karp's algorithm. \def\lsqrtn{\hbox{$\lfloor\sqrt n\,\rfloor$}}% \def\usqrtn{\hbox{$\lfloor\sqrt{n+1}+{1\over2}\rfloor$}}% The final algorithm we shall consider takes yet another approach to spanning tree minimization. It operates in two distinct stages: Stage~1 creates small fragments of the minimum tree, working locally with the edges that lead out of each fragment instead of dealing with the full set of edges at once as in Kruskal's method. As soon as the number of component fragments has been reduced from $n$ to \lsqrtn, stage~2 begins. Stage~2 runs through the remaining edges and builds a $\lsqrtn\times\lsqrtn$ matrix, which represents the problem of finding a minimum spanning tree on the remaining \lsqrtn\ components. A simple $O(\sqrt n\,)^2=O(n)$ algorithm then completes the job. The philosophy underlying stage~1 is that an edge leading out of a vertex in a small component is likely to lead to a vertex in another component, rather than in the same one. Thus each delete-min operation tends to be productive. Karp and Tarjan proved [{\sl Journal of Algorithms\/ @^Karp, Richard Manning@> @^Tarjan, Robert Endre@> \bf1} (1980), 374--393] that the average running time on a random graph with $n$ vertices and $m$ edges will be $O(m)$. The philosophy underlying stage~2 is that the problem on an initially sparse graph eventually reduces to a problem on a smaller but dense graph that is best solved by a different method. @<Sub...@>= unsigned long cher_tar_kar(g) Graph *g; {@+@<Local variables for |cher_tar_kar|@>@;@# mems=0; @<Do stage 1 of |cher_tar_kar|@>; if (verbose) printf(" [Stage 1 has used %ld mems]\n",mems); @<Do stage 2 of |cher_tar_kar|@>; return tot_len; } @ We say that a fragment is {\sl large} if it contains \usqrtn\ or more vertices. As soon as a fragment becomes large, stage~1 stops trying to extend it. There cannot be more than \lsqrtn\ large fragments, because $(\lsqrtn+1)\usqrtn>n$. The other fragments are called {\sl small}. Stage~1 keeps a list of all the small fragments. Initially this list contains $n$ fragments consisting of one vertex each. The algorithm repeatedly looks at the first fragment on its list, and finds the smallest edge leading to another fragment. These two fragments are removed from the list and combined. The resulting fragment is put at the end of the list if it is still small, or put onto another list if it is large. @<Local variables for |ch...@>= register Vertex *s,*t; /* beginning and end of the small list */ Vertex *large_list; /* beginning of the list of large fragments */ long frags; /* current number of fragments, large and small */ unsigned long tot_len=0; /* total length of all edges in fragments */ register Vertex *u,*v; /* registers for list manipulation */ register Arc *a; /* and another */ register long j,k; /* index registers for stage 2 */ @ We need to make |lo_sqrt| global so that the |note_edge| procedure below can access it. @<Glob...@>= long lo_sqrt,hi_sqrt; /* \lsqrtn\ and \usqrtn\ */ @ There is a nonobvious way to compute \usqrtn\ and \lsqrtn. Since $\sqrt n$ is small and arithmetic is mem-free, the author couldn't resist writing the |for| loop shown here. Of course, different ground rules for counting mems would be appropriate if this sort of computing were a critical factor in the running time. @^discussion of \\{mems}@> @<Do stage 1 of |cher_tar_kar|@>= o,frags=g->n; for (hi_sqrt=1;hi_sqrt*(hi_sqrt+1)<=frags;hi_sqrt++) ; if (hi_sqrt*hi_sqrt<=frags) lo_sqrt=hi_sqrt; else lo_sqrt=hi_sqrt-1; large_list=NULL; @<Create the small list@>; while (frags>lo_sqrt) { @<Combine the first fragment on the small list with its nearest neighbor@>; frags--; } @ To represent fragments, we will use several utility fields already defined above. The |lsib| and |rsib| pointers are used between fragments in the small list, which is doubly linked; |s|~points to the first small fragment, |s->rsib| to the next, \dots, |t->lsib| to the second-from-last, and |t| to the last. The pointer fields |s->lsib| and |t->rsib| are undefined. The |large_list| is singly linked via |rsib| pointers, terminating with |NULL|. The |csize| field of each fragment tells how many vertices it contains. The |comp| field of each vertex is |NULL| if this vertex represents a fragment (i.e., if this vertex is in the small list or |large_list|); otherwise it points to another vertex that is closer to the fragment representative. Finally, the |pq| pointer of each fragment points to the header node of its priority queue, which is a binomial queue containing all unlooked-at arcs that originate from vertices in the fragment. This pointer is identical to the |newarc| pointer already set up. In a production implementation, we wouldn't need |pq| as a separate field; it would be part of a vertex record. So we do not pay any mems for referring to it. @^discussion of \\{mems}@> @d pq newarc @<Create the small...@>= o,s=g->vertices; for (v=s;v<s+frags;v++) { if (v>s) { o,v->lsib=v-1;@+o,(v-1)->rsib=v; } o,v->comp=NULL; o,v->csize=1; o,v->pq->qcount=0; /* the binomial queue is initially empty */ for (o,a=v->arcs;a;o,a=a->next) qenque(v->pq,a); } t=v-1; @ @<Combine the first fragment...@>= v=s; o,s=s->rsib; /* remove |v| from small list */ do@+{a=qdel_min(v->pq); if (a==NULL) return INFINITY; /* the graph isn't connected */ o,u=a->tip; while (o,u->comp) u=u->comp; /* find the fragment pointed to */ }@+while (u==v); /* repeat until a new fragment is found */ if (verbose) @<Report the new edge verbosely@>; o,tot_len+=a->len; o,v->comp=u; qmerge(u->pq,v->pq); o,old_size=u->csize; o,new_size=old_size+v->csize; o,u->csize=new_size; @<Move |u| to the proper list position@>; @ @<Local variables for |cher...@>= long old_size,new_size; /* size of fragment |u|, before and after */ @ Here is a fussy part of the program. We have just merged the small fragment |v| into another fragment~|u|. If |u| was already large, there's nothing to do (except to check if the small list has just become empty). Otherwise we need to move |u| to the end of the small list, or we need to put it onto the large list. All these cases are special, if we want to avoid unnecessary memory references; so let's hope we get them right. @<Move |u|...@>= if (old_size>=hi_sqrt) { /* |u| was large */ if (t==v) s=NULL; /* small list just became empty */ }@+else if (new_size<hi_sqrt) { /* |u| was and still is small */ if (u==t) goto fin; /* |u| is already where we want it */ if (u==s) o,s=u->rsib; /* remove |u| from front */ else { ooo,u->rsib->lsib=u->lsib; /* detach |u| from middle */ o,u->lsib->rsib=u->rsib; /* do you follow the mem-counting here? */ @^discussion of \\{mems}@> } o,t->rsib=u; /* insert |u| at the end */ o,u->lsib=t; t=u; }@+else { /* |u| has just become large */ if (u==t) { if (u==s) goto fin; /* well, keep it small, we're done anyway */ o,t=u->lsib; /* remove |u| from end */ }@+else if (u==s) o,s=u->rsib; /* remove |u| from front */ else { ooo,u->rsib->lsib=u->lsib; /* detach |u| from middle */ o,u->lsib->rsib=u->rsib; } o,u->rsib=large_list;@+large_list=u; /* make |u| large */ } fin:; @ We don't have room in our binomial queues to keep track of both endpoints of the arcs. But the arcs occur in pairs, and by looking at the address of |a| we can tell whether the matching arc is |a+1| or |a-1|. (See the explanation in {\sc GB\_\,GRAPH}.) @<Report the new edge verbosely@>= report((edge_trick&(siz_t)a? a-1: a+1)->tip,a->tip,a->len); @*Cheriton, Tarjan, and Karp's algorithm (continued). And now for the second part of the algorithm. Here we need to find room for a $\lsqrtn\times\lsqrtn$ matrix of edge lengths; we will use random access into the |z| utility fields of vertex records, since these haven't been used for anything yet by |cher_tar_kar|. We can also use the |v| utility fields to record the arcs that are the source of the best lengths, since this was the |lsib| field (no longer needed). The program doesn't count mems for updating that field, since it considers its goal to be simply the calculation of minimum spanning tree length; the actual edges of the minimum spanning tree are computed only for |verbose| mode. (We want to see how competitive |cher_tar_kar| is when we streamline it as much as possible.) @^discussion of \\{mems}@> In stage 2, the vertices will be assigned integer index numbers between 0 and $\lsqrtn-1$. We'll put this into the |csize| field, which is no longer needed, and call it |findex|. @d findex csize @d matx(j,k) (gv+((j)*lo_sqrt+(k)))->z.I /* distance between fragments |j| and |k| */ @d matx_arc(j,k) (gv+((j)*lo_sqrt+(k)))->v.A /* arc corresponding to |matx(j,k)| */ @d INF 30000 /* upper bound on all edge lengths */ @<Do stage 2 of |cher_tar_kar|@>= gv=g->vertices; /* the global variable |gv| helps access auxiliary memory */ @<Map all vertices to their index numbers@>; @<Create the reduced matrix by running through all remaining edges@>; @<Execute Prim's algorithm on the reduced matrix@>; @ The vertex-mapping algorithm is $O(n)$ because each non-null |comp| link is examined at most three times. We set the |comp| field to null as an indication that |findex| has been set. @<Map all...@>= if (s==NULL) s=large_list; else o,t->rsib=large_list; for (k=0,v=s;v;o,v=v->rsib,k++) o,v->findex=k; for (v=g->vertices;v<g->vertices+g->n;v++) if (o,v->comp) { for (t=v->comp;o,t->comp;t=t->comp) ; o,k=t->findex; for (t=v;o,u=t->comp;t=u) { o,t->comp=NULL; o,t->findex=k; } } @ @<Create the reduced matrix by running through all remaining edges@>= for (j=0;j<lo_sqrt;j++) for (k=0;k<lo_sqrt;k++) o,matx(j,k)=INF; for (kk=0;s;o,s=s->rsib,kk++) qtraverse(s->pq,note_edge); @ The |note_edge| procedure ``visits'' every edge in the binomial queues traversed by |qtraverse| in the preceding code. Global variable |kk|, which would be a global register in a production version, is the index of the fragment from which this arc emanates. @<Procedures to be declared early@>= void note_edge(a) Arc *a; {@+register long k; oo,k=a->tip->findex; if (k==kk) return; if (oo,a->len<matx(kk,k)) { o,matx(kk,k)=a->len; o,matx(k,kk)=a->len; matx_arc(kk,k)=matx_arc(k,kk)=a; } } @ As we work on the final subproblem of size $\lsqrtn\times\lsqrtn$, we'll have a short vector that tells us the distance to each fragment that hasn't yet been joined up with fragment~0. The vector has |-1| in positions that already have been joined up. In a production version, we could keep this in row~0 of |matx|. @<Glob...@>= long kk; /* current fragment */ long distance[100]; /* distances to at most \lsqrtn\ unhit fragments */ Arc *dist_arc[100]; /* the corresponding arcs, for |verbose| mode */ @ The last step, as suggested by Prim, repeatedly updates @^Prim, Robert Clay@> the distance table against each row of the matrix as it is encountered. This is the algorithm of choice to find the minimum spanning tree of a complete graph. @<Execute Prim's algorithm on the reduced matrix@>= {@+long d; /* shortest entry seen so far in |distance| vector */ o,distance[0]=-1; d=INF; for (k=1;k<lo_sqrt;k++) { o,distance[k]=matx(0,k); dist_arc[k]=matx_arc(0,k); if (distance[k]<d) d=distance[k],j=k; } while (frags>1) @<Connect fragment 0 with fragment |j|, since |j| is the column achieving the smallest distance, |d|; also compute |j| and |d| for the next round@>; } @ @<Connect fragment 0...@>= { if (d==INF) return INFINITY; /* the graph isn't connected */ o,distance[j]=-1; /* fragment |j| now will join up with fragment 0 */ tot_len+=d; if (verbose) { a=dist_arc[j]; @<Report the new edge verbosely@>; } frags--; d=INF; for (k=1;k<lo_sqrt;k++) if (o,distance[k]>=0) { if (o,matx(j,k)<distance[k]) { o,distance[k]=matx(j,k); dist_arc[k]=matx_arc(j,k); } if (distance[k]<d) d=distance[k],kk=k; } j=kk; } @* Conclusions. The winning algorithm, of the four methods considered here, on problems of the size considered here, with respect to mem counting, is clearly Jarn{\'\i}k/Prim with binary heaps. Second is Kruskal with radix sorting, on sparse graphs, but the Fibonacci heap method beats it on dense graphs. Procedure |cher_tar_kar| never comes close, although every step it takes seems to be reasonably sensible and efficient, and although the implementation above gives it the benefit of every doubt when counting its mems. It apparently loses because it more or less gives up a factor of~2 by dealing with each edge twice; the other methods put very little effort into discarding an arc whose mate has already been processed. But it is important to realize that mem counting is not the whole story. Further tests were made on a Sun SPARCstation~2, in order to measure the true @^discussion of \\{mems}@> running times when all the complications of pipelining, caching, and compiler optimization are taken into account. These runs showed that Kruskal's algorithm was actually best, at least on the particular system tested: $$\advance\abovedisplayskip-5pt \advance\belowdisplayskip-5pt \advance\baselineskip-1pt \vbox{\halign{#\hfil&&\quad\hfil#\cr \hfill optimization level&\.{-g}\hfil&\.{-O2}\hfil&\.{-O3}\hfil&mems\hfil\cr \noalign{\vskip2pt} Kruskal/radix&132&111&111&8379\cr Jarn{\'\i}k/Prim/binary&307&226&212&7972\cr Jarn{\'\i}k/Prim/Fibonacci&432&350&333&11736\cr Cheriton/Tarjan/Karp&686&509&492&17770\cr}}$$ (Times are shown in seconds per 100,000 runs with the default graph |miles(100,0,0,0,0,10,0)|. Optimization level \.{-O4} gave the same results as \.{-O3}. Optimization does not change the mem count.) Thus the Kruskal procedure used only about 160 nanoseconds per mem, without optimization, and about 130 with; the others used about 380 to 400 ns/mem without optimization, 270 to 300 with. The mem measure gave consistent readings for the three ``sophisticated'' data structures, but the ``na{\"\i}ve'' Kruskal method blended better with hardware. The complete graph |miles(100,0,0,0,0, 99,0)|, obtained by specifying option \.{-d100}, gave somewhat different statistics: $$\advance\abovedisplayskip-5pt \advance\belowdisplayskip-5pt \advance\baselineskip-1pt \vbox{\halign{#\hfil&&\quad\hfil#\cr \hfill optimization level&\.{-g}\hfil&\.{-O2}\hfil&\.{-O3}\hfil&mems\hfil\cr \noalign{\vskip2pt} Kruskal/radix&1846&1787&1810&63795\cr Jarn{\'\i}k/Prim/binary&2246&1958&1845&50594\cr Jarn{\'\i}k/Prim/Fibonacci&2675&2377&2248&59050\cr Cheriton/Tarjan/Karp&8881&6964&6909&175519\cr}}$$ % Kruskal 285 ns/mem; others 360--450, except unoptimized CTK was 536! Now the identical machine instructions took significantly longer per mem---presumably because of cache misses, although the frequency of conditional jump instructions might also be a factor. Careful analyses of these phenomena should be instructive. Future computers are expected to be more nearly limited by memory speed; therefore the running time per mem is likely to become more uniform between methods, although cache performance will probably always be a factor. The |krusk| procedure might go even faster if it were given a streamlined union/find algorithm. Or would such ``streamlining'' negate some of its present efficiency? @* Index. We close with a list that shows where the identifiers of this program are defined and used. A special index term, `discussion of \\{mems}', indicates sections where there are nontrivial comments about instrumenting a \CEE/ program in the manner being recommended here. ��������������������������������������������������������multiply.w������������������������������������������������������������������������������������������0000444�0001750�0001750�00000024212�05440400624�011244� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{MULTIPLY} \prerequisite{GB\_\,GATES} @* Introduction. This demonstration program uses graphs constructed by the |prod| procedure in the {\sc GB\_\,GATES} module to produce an interactive program called \.{multiply}, which multiplies and divides small numbers the slow way---by simulating the behavior of a logical circuit, one gate at a time. The program assumes that \UNIX/ conventions are being used. Some code in sections listed under `\UNIX/ dependencies' in the index might need to change if this program is ported to other operating systems. \def\<#1>{$\langle${\rm#1}$\rangle$} To run the program under \UNIX/, say `\.{multiply} $m$ $n$ [|seed|]', where $m$ and $n$ are the sizes of the numbers to be multiplied, in bits, and where |seed| is given if and only if you want the multiplier to be a special-purpose circuit for multiplying a given $m$-bit number by a randomly chosen $n$-bit constant. The program will prompt you for two numbers (or for just one, if the random constant option has been selected), and it will use the gate network to compute their product. Then it will ask for more input, and so on. @ Here is the general layout of this program, as seen by the \CEE/ compiler: @^UNIX dependencies@> @p #include "gb_graph.h" /* the standard GraphBase data structures */ #include "gb_gates.h" /* routines for gate graphs */ @h@# @<Global variables@>@; @<Handy subroutines@>@; main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ { @<Declare variables that ought to be in registers@>; @<Obtain |m|, |n|, and optional |seed| from the command line@>; @<Make sure |m| and |n| are valid; generate the |prod| graph |g|@>; if (seed<0) /* no seed given */ printf("Here I am, ready to multiply %ld-bit numbers by %ld-bit numbers.\n", m,n); else { g=partial_gates(g,m,0L,seed,buffer); if (g) { @<Set |y| to the decimal value of the second input@>; printf("OK, I'm ready to multiply any %ld-bit number by %s.\n",m,y); }@+else { /* there was enough memory to make the original |g|, but not enough to reduce it; this probably can't happen, but who knows? */ printf("Sorry, I couldn't process the graph (trouble code %ld)!\n", panic_code); return -9; } } printf("(I'm simulating a logic circuit with %ld gates, depth %ld.)\n", g->n,depth(g)); while(1) { @<Prompt for one or two numbers; |break| if unsuccessful@>; @<Use the network to compute the product@>; printf("%sx%s=%s%s.\n",x,y,(strlen(x)+strlen(y)>35?"\n ":""),z); } return 0; /* normal exit */ } @ @<Make sure |m| and |n| are valid; generate the |prod| graph |g|@>= if (m<2) m=2; if (n<2) n=2; if (m>999 || n>999) { printf("Sorry, I'm set up only for precision less than 1000 bits.\n"); return -1; } if ((g=prod(m,n))==NULL) { printf("Sorry, I couldn't generate the graph (not enough memory for %s)!\n", panic_code==no_room? "the gates": panic_code==alloc_fault? "the wires": "local optimization"); return -3; } @ To figure the maximum length of strings |x| and |y|, we note that $2^{999}\approx5.4\times10^{300}$. @<Glob...@>= Graph *g; /* graph that defines a logical network for multiplication */ long m,n; /* length of binary numbers to be multiplied */ long seed; /* optional seed value, or $-1$ */ char x[302], y[302], z[603]; /* input and output numbers, as decimal strings */ char buffer[2000]; /* workspace for communication between routines */ @ @<Declare variables...@>= register char *p,*q,*r; /* pointers for string manipulation */ register long a,b; /* amounts being carried over while doing radix conversion */ @ @<Obtain |m|, |n|, and...@>= @^UNIX dependencies@> if (argc<3 || argc>4 || sscanf(argv[1],"%ld",&m)!=1 || sscanf(argv[2],"%ld",&n)!=1) { fprintf(stderr,"Usage: %s m n [seed]\n",argv[0]); return -2; } if (m<0) m=-m; /* maybe the user attached |'-'| to the argument */ if (n<0) n=-n; seed=-1; if (argc==4 && sscanf(argv[3],"%ld",&seed)==1 && seed<0) seed=-seed; @ This program may not be user-friendly, but at least it is polite. @d prompt(s) {@+printf(s);@+fflush(stdout); /* make sure the user sees the prompt */ if (fgets(buffer,999,stdin)==NULL) break;@+} @d retry(s,t) {@+printf(s);@+goto t;@+} @<Prompt...@>= step1: prompt("\nNumber, please? "); for (p=buffer;*p=='0';p++) ; /* bypass leading zeroes */ if (*p=='\n') { if (p>buffer) p--; /* zero is acceptable */ else break; /* empty input terminates the run */ } for (q=p;*q>='0' && *q<='9';q++) ; /* check for digits */ if (*q!='\n') retry( "Excuse me... I'm looking for a nonnegative sequence of decimal digits.", step1); *q=0; if (strlen(p)>301) retry("Sorry, that's too big.",step1); strcpy(x,p); if (seed<0) { @<Do the same thing for |y| instead of |x|@>; } @ @<Do the same...@>= step2: prompt("Another? "); for (p=buffer;*p=='0';p++) ; /* bypass leading zeroes */ if (*p=='\n') { if (p>buffer) p--; /* zero is acceptable */ else break; /* empty input terminates the run */ } for (q=p;*q>='0' && *q<='9';q++) ; /* check for digits */ if (*q!='\n') retry( "Excuse me... I'm looking for a nonnegative sequence of decimal digits.", step2); *q=0; if (strlen(p)>301) retry("Sorry, that's too big.",step2); strcpy(y,p); @ The binary value chosen at random by |partial_gates| appears as a string of 0s and 1s in |buffer|, in little-endian order. We compute the corresponding decimal value by repeated doubling. If the value turns out to be zero, the whole network will have collapsed. Otherwise, however, the |m| inputs from the first operand will all remain present, because they all affect the output. @<Set |y| to the decimal value of the second input@>= *y='0';@+*(y+1)=0; /* now |y| is |"0"| */ for (r=buffer+strlen(buffer)-1;r>=buffer;r--) { /* we will set $y=2y+t$ where $t$ is the next bit, |*r| */ if (*y>='5') a=0,p=y; else a=*y-'0',p=y+1; for (q=y;*p;a=b,p++,q++) { if (*p>='5') { b=*p-'5'; *q=2*a+'1'; }@+else { b=*p-'0'; *q=2*a+'0'; } } if (*r=='1') *q=2*a+'1'; else *q=2*a+'0'; *++q=0; /* terminate the string */ } if (strcmp(y,"0")==0) { printf("Please try another seed value; %d makes the answer zero!\n",seed); return(-5); } @* Using the network. The reader of the code in the previous section will have noticed that we are representing high-precision decimal numbers as strings. We might as well do that, since the only operations we need to perform on them are input, output, doubling, and halving. In fact, arithmetic on strings is kind of fun, if you like that sort of thing. Here is a subroutine that converts a decimal string to a binary string. The decimal string is big-endian as usual, but the binary string is little-endian. The decimal string is decimated in the process; it should end up empty, unless the original value was too big. @<Handy subroutines@>= decimal_to_binary(x,s,n) char *x; /* decimal string */ char *s; /* binary string */ long n; /* length of |s| */ {@+register long k; register char *p,*q; /* pointers for string manipulation */ register long r; /* remainder */ for (k=0;k<n;k++,s++) { if (*x==0) *s='0'; else { /* we will divide |x| by 2 */ if (*x>'1') p=x,r=0; else p=x+1,r=*x-'0'; for (q=x;*p;p++,q++) { r=10*r+*p-'0'; *q=(r>>1)+'0'; r=r&1; } *q=0; /* terminate string |x| */ *s='0'+r; } } *s=0; /* terminate the output string */ } @ @<Use the network to compute the product@>= strcpy(z,x); decimal_to_binary(z,buffer,m); if (*z) { printf("(Sorry, %s has more than %ld bits.)\n",x,m); continue; } if (seed<0) { strcpy(z,y); decimal_to_binary(z,buffer+m,n); if (*z) { printf("(Sorry, %s has more than %ld bits.)\n",y,n); continue; } } if (gate_eval(g,buffer,buffer)<0) { printf("??? An internal error occurred!"); return 666; /* this can't happen */ } @<Convert the binary number in |buffer| to the decimal string |z|@>; @ The remaining task is almost identical to what we needed to do when computing the value of |y| after a random seed was specified. But this time the binary number in |buffer| is big-endian. @<Convert the binary number in |buffer| to the decimal string |z|@>= *z='0';@+*(z+1)=0; for (r=buffer;*r;r++) { /* we'll set $z=2z+t$ where $t$ is the next bit, |*r| */ if (*z>='5') a=0,p=z; else a=*z-'0',p=z+1; for (q=z;*p;a=b,p++,q++) { if (*p>='5') { b=*p-'5'; *q=2*a+'1'; }@+else { b=*p-'0'; *q=2*a+'0'; } } if (*r=='1') *q=2*a+'1'; else *q=2*a+'0'; *++q=0; /* terminate the string */ } @* Calculating the depth. The depth of a gate network produced by {\sc GB\_\,GATES} is easily discovered by making one pass over the vertices. An input gate or a constant has depth~0; every other gate has depth one greater than the maximum of its inputs. This routine is more general than it needs to be for the circuits output by |prod|. The result of a latch is considered to have depth~0. Utility field |u.I| is set to the depth of each individual gate. @d dp u.I @<Handy...@>= long depth(g) Graph *g; /* graph with gates as vertices */ {@+register Vertex *v; /* the current vertex of interest */ register Arc *a; /* the current arc of interest */ long d; /* depth of current vertex */ if (!g) return -1; /* no graph supplied! */ for (v=g->vertices; v<g->vertices+g->n; v++) { switch (v->typ) { /* branch on type of gate */ case 'I': case 'L': case 'C': v->dp=0;@+break; default: @<Set |d| to the maximum depth of an operand of |v|@>; v->dp=1+d; } } @<Set |d| to the maximum depth of an output of |g|@>; return d; } @ @<Set |d| to the maximum depth of an operand of |v|@>= d=0; for (a=v->arcs; a; a=a->next) if (a->tip->dp>d) d=a->tip->dp; @ @<Set |d| to the maximum depth of an output of |g|@>= d=0; for (a=g->outs; a; a=a->next) if (!is_boolean(a->tip) && a->tip->dp>d) d=a->tip->dp; @* Index. Finally, here's a list that shows where the identifiers of this program are defined and used. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������queen.w���������������������������������������������������������������������������������������������0000444�0001750�0001750�00000004040�05440401115�010473� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{QUEEN} @* Queen moves. This is a short demonstration of how to generate and traverse graphs with the Stanford GraphBase. It creates a graph with 12 vertices, representing the cells of a $3\times4$ rectangular board; two cells are considered adjacent if you can get from one to another by a queen move. Then it prints a description of the vertices and their neighbors, on the standard output file. An ASCII file called \.{queen.gb} is also produced. Other programs can obtain a copy of the queen graph by calling |restore_graph("queen.gb")|. You might find it interesting to compare the output of {\sc QUEEN} with the contents of \.{queen.gb}; the former is intended to be readable by human beings, the latter by computers. @p #include "gb_graph.h" /* we use the {\sc GB\_\,GRAPH} data structures */ #include "gb_basic.h" /* we test the basic graph operations */ #include "gb_save.h" /* and we save our results in ASCII format */ @# main() {@+Graph *g,*gg,*ggg; g=board(3L,4L,0L,0L,-1L,0L,0L); /* a graph with rook moves */ gg=board(3L,4L,0L,0L,-2L,0L,0L); /* a graph with bishop moves */ ggg=gunion(g,gg,0L,0L); /* a graph with queen moves */ save_graph(ggg,"queen.gb"); /* generate an ASCII file for |ggg| */ @<Print the vertices and edges of |ggg|@>; return 0; /* normal exit */ } @ @<Print the vertices and edges of |ggg|@>= if (ggg==NULL) printf("Something went wrong (panic code %ld)!\n",panic_code); else { register Vertex *v; /* current vertex being visited */ printf("Queen Moves on a 3x4 Board\n\n"); printf(" The graph whose official name is\n%s\n", ggg->id); printf(" has %ld vertices and %ld arcs:\n\n", ggg->n, ggg->m); for (v=ggg->vertices; v<ggg->vertices+ggg->n; v++) { register Arc *a; /* current arc from |v| */ printf("%s\n", v->name); for (a=v->arcs; a; a=a->next) printf(" -> %s, length %ld\n", a->tip->name, a->len); } } @* Index. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������roget_components.w����������������������������������������������������������������������������������0000444�0001750�0001750�00000042620�10264524671�012766� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{ROGET\_\,COMPONENTS} \def\<#1>{$\langle${\rm#1}$\rangle$} \prerequisite{GB\_\,ROGET} @* Strong components. This demonstration program computes the strong components of GraphBase graphs derived from Roget's Thesaurus, using a variant of Tarjan's algorithm [R. E. Tarjan, ``Depth-first @^Tarjan, Robert Endre@> search and linear graph algorithms,'' {\sl SIAM Journal on Computing\/ \bf1} (1972), 146--160]. We also determine the relationships between strong components. Two vertices belong to the same strong component if and only if they are reachable from each other via directed paths. We will print the strong components in ``reverse topological order''; that is, if |v| is reachable from~|u| but |u| is not reachable from~|v|, the strong component containing~|v| will be listed before the strong component containing~|u|. Vertices from the |roget| graph are identified both by name and by category number. @d specs(v) (filename? v-g->vertices+1L: v->cat_no), v->name /* category number and category name */ @ We permit command-line options in \UNIX/ style so that a variety of graphs can be studied: The user can say `\.{-n}\<number>', `\.{-d}\<number>', `\.{-p}\<number>', and/or `\.{-s}\<number>' to change the default values of the parameters in the graph |roget(n,d,p,s)|. Or `\.{-g}\<filename>' to change the graph itself. @^UNIX dependencies@> @p #include "gb_graph.h" /* the GraphBase data structures */ #include "gb_roget.h" /* the |roget| routine */ #include "gb_save.h" /* |restore_graph| */ @h@# @<Global variables@>@; main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ {@+Graph *g; /* the graph we will work on */ register Vertex *v; /* the current vertex of interest */ unsigned long n=0; /* the desired number of vertices (0 means infinity) */ unsigned long d=0; /* the minimum distance between categories in arcs */ unsigned long p=0; /* 65536 times the probability of rejecting an arc */ long s=0; /* the random number seed */ char *filename=NULL; /* external graph substituted for |roget| */ @<Scan the command-line options@>; g=(filename? restore_graph(filename): roget(n,d,p,s)); if (g==NULL) { fprintf(stderr,"Sorry, can't create the graph! (error code %ld)\n", panic_code); return -1; } printf("Reachability analysis of %s\n\n",g->id); @<Perform Tarjan's algorithm on |g|@>; return 0; /* normal exit */ } @ @<Scan the command-line options@>= while (--argc) { @^UNIX dependencies@> if (sscanf(argv[argc],"-n%lu",&n)==1) ; else if (sscanf(argv[argc],"-d%lu",&d)==1) ; else if (sscanf(argv[argc],"-p%lu",&p)==1) ; else if (sscanf(argv[argc],"-s%ld",&s)==1) ; else if (strncmp(argv[argc],"-g",2)==0) filename=argv[argc]+2; else { fprintf(stderr,"Usage: %s [-nN][-dN][-pN][-sN][-gfoo]\n",argv[0]); return -2; } } @ Tarjan's algorithm is inherently recursive. We will implement the recursion explicitly via linked lists, instead of using \CEE/'s runtime stack, because some computer systems bog down in the presence of deeply nested recursion. Each vertex goes through three stages during the algorithm: First it is ``unseen''; then it is ``active''; finally it becomes ``settled,'' when it has been assigned to a strong component. The data structures that represent the current state of the algorithm are implemented by using five of the utility fields in each vertex: |rank|, |parent|, |untagged|, |link|, and |min|. We will consider each of these in turn. @ First is the integer |rank| field, which is zero when a vertex is unseen. As soon as the vertex is first examined, it becomes active and its |rank| becomes and remains nonzero. Indeed, the $k$th vertex to become active will receive rank~$k$. When a vertex finally becomes settled, its rank is reset to infinity. It's convenient to think of Tarjan's algorithm as a simple adventure game in which we want to explore all the rooms of a cave. Passageways between the rooms allow one-way travel only. When we come into a room for the first time, we assign a new number to that room; this is its rank. Later on we might happen to enter the same room again, and we will notice that it has nonzero rank. Then we'll be able to make a quick exit, saying ``we've already been here.'' (The extra complexities of computer games, like dragons that might need to be vanquished, do not arise.) @d rank z.I /* the |rank| of a vertex is stored in utility field |z| */ @<Glob...@>= long nn; /* the number of vertices that have been seen */ @ The active vertices will always form an oriented tree, whose arcs are a subset of the arcs in the original graph. A tree arc from |u| to~|v| will be represented by |v->parent==u|. Every active vertex has a parent, which is usually another active vertex; the only exception is the root of the tree, whose |parent| is |NULL|. In the cave analogy, the ``parent'' of room |v| is the room we were in immediately before entering |v| the first time. By following parent pointers, we will be able to leave the cave whenever we want. As soon as a vertex becomes settled, its |parent| field changes significance. Then |v->parent| is set equal to the unique representative of the strong component containing vertex~|v|. Thus two settled vertices will belong to the same strong component if and only if they have the same |parent|. @d parent y.V /* the |parent| of a vertex is stored in utility field |y| */ @ All arcs in the original directed graph are explored systematically during a depth-first search. Whenever we look at an arc, we tag it so that we won't need to explore it again. In a cave, for example, we might mark each passageway between rooms once we've tried to go through it. The algorithm doesn't actually place a tag on its |Arc| records; instead, each vertex |v| has a pointer |v->untagged| that leads to all hitherto-unexplored arcs from~|v|. The arcs of the list that appear between |v->arcs| and |v->untagged| are the ones already examined. @d untagged x.A /* the |untagged| field points to an |Arc| record, or |NULL| */ @ The algorithm maintains two special stacks: |active_stack| contains all the currently active vertices, and |settled_stack| contains all the currently settled vertices. Each vertex has a |link| field that points to the vertex that is next lower on its stack, or to |NULL| if the vertex is at the bottom. The vertices on |active_stack| always appear in increasing order of rank from bottom to top. @d link w.V /* the |link| field of a vertex occupies utility field |w| */ @<Glob...@>= Vertex * active_stack; /* the top of the stack of active vertices */ Vertex * settled_stack; /* the top of the stack of settled vertices */ @ Finally there's a |min| field, which is the tricky part that makes everything work. If vertex~|v| is unseen or settled, its |min| field is irrelevant. Otherwise |v->min| points to the active vertex~|u| of smallest rank having the following property: Either |u==v| or there is a directed path from |v| to |u| consisting of zero or more mature tree arcs followed by a single non-tree arc. What is a tree arc, you ask. And what is a mature arc? Good questions. At the moment when arcs of the graph are tagged, we classify them either as tree arcs (if they correspond to a new |parent| link in the tree of active nodes) or non-tree arcs (otherwise). A tree arc becomes mature when it is no longer on the path from the root to the current vertex being explored. We also say that a vertex becomes mature when it is no longer on that path. All arcs from a mature vertex have been tagged. We said before that every vertex is initially unseen, then active, and finally settled. With our new definitions, we see further that every arc starts out untagged, then it becomes either a non-tree arc or a tree arc. In the latter case, the arc begins as an immature tree arc and eventually matures. Just believe these definitions, for now. All will become clear soon. @d min v.V /* the |min| field of a vertex occupies utility field |v| */ @ Depth-first search explores a graph by systematically visiting all vertices and seeing what they can lead to. In Tarjan's algorithm, as we have said, the active vertices form an oriented tree. One of these vertices is called the current vertex. If the current vertex still has an arc that hasn't been tagged, we tag one such arc and there are two cases: Either the arc leads to an unseen vertex, or it doesn't. If it does, the arc becomes a tree arc; the previously unseen vertex becomes active, and it becomes the new current vertex. On the other hand if the arc leads to a vertex that has already been seen, the arc becomes a non-tree arc and the current vertex doesn't change. Finally there will come a time when the current vertex~|v| has no untagged arcs. At this point, the algorithm might decide that |v| and all its descendants form a strong component. Indeed, this condition turns out to be true if and only if |v->min==v|; a proof appears below. If so, |v| and all its descendants become settled, and they leave the tree. If not, the tree arc from |v|'s parent~|u| to~|v| becomes mature, so the value of |v->min| is used to update the value of |u->min|. In both cases, |v| becomes mature and the new current vertex will be the parent of~|v|. Notice that only the value of |u->min| needs to be updated, when the arc from |u| to~|v| matures; all other values |w->min| stay the same, because a newly mature arc has no mature predecessors. The cave analogy helps to clarify the situation: If there's no way out of the subcave starting at~|v| unless we come back through |v| itself, and if we can get back to |v| from all its descendants, then room~|v| and its descendants will become a strong component. Once such a strong component is identified, we close it off and don't explore that subcave any further. If |v| is the root of the tree, it always has |v->min==v|, so it will always define a new strong component at the moment it matures. Then the depth-first search will terminate, since |v|~has no parent. But Tarjan's algorithm will press on, trying to find a vertex~|u| that is still unseen. If such a vertex exists, a new depth-first search will begin with |u| as the root. This process keeps on going until at last all vertices are happily settled. The beauty of this algorithm is that it all works very efficiently when we organize it as follows: @<Perform Tarjan's algorithm on |g|@>= @<Make all vertices unseen and all arcs untagged@>; for (vv=g->vertices; vv<g->vertices+g->n; vv++) if (vv->rank==0) /* |vv| is still unseen */ @<Perform a depth-first search with |vv| as the root, finding the strong components of all unseen vertices reachable from~|vv|@>; @<Print out one representative of each arc that runs between strong components@>; @ @<Glob...@>= Vertex *vv; /* sweeps over all vertices, making sure none is left unseen */ @ It's easy to get the data structures started, according to the conventions stipulated above. @<Make all vertices unseen...@>= for (v=g->vertices+g->n-1; v>=g->vertices; v--) { v->rank=0; v->untagged=v->arcs; } nn=0; active_stack=settled_stack=NULL; @ The task of starting a depth-first search isn't too bad either. Throughout this part of the algorithm, variable~|v| will point to the current vertex. @<Perform a depth-first search with |vv| as the root...@>= { v=vv; v->parent=NULL; @<Make vertex |v| active@>; do @<Explore one step from the current vertex~|v|, possibly moving to another current vertex and calling~it~|v|@>@; while (v!=NULL); } @ @<Make vertex |v| active@>= v->rank=++nn; v->link=active_stack; active_stack=v; v->min=v; @ Now things get interesting. But we're just doing what any well-organized spelunker would do when calmly exploring a cave. There are three main cases, depending on whether the current vertex stays where it is, moves to a new child, or backtracks to a parent. @<Explore one step from the current vertex~|v|, possibly moving to another current vertex and calling~it~|v|@>= {@+register Vertex *u; /* a vertex adjacent to |v| */ register Arc *a=v->untagged; /* |v|'s first remaining untagged arc, if any */ if (a) { u=a->tip; v->untagged = a->next; /* tag the arc from |v| to |u| */ if (u->rank) { /* we've seen |u| already */ if (u->rank < v->min->rank) v->min=u; /* non-tree arc, just update |v->min| */ }@+else { /* |u| is presently unseen */ u->parent = v; /* the arc from |v| to |u| is a new tree arc */ v = u; /* |u| will now be the current vertex */ @<Make vertex |v| active@>; } }@+else { /* all arcs from |v| are tagged, so |v| matures */ u=v->parent; /* prepare to backtrack in the tree */ if (v->min==v) @<Remove |v| and all its successors on the active stack from the tree, and mark them as a strong component of the graph@>@; else /* the arc from |u| to |v| has just matured, making |v->min| visible from |u| */@, if (v->min->rank < u->min->rank) u->min=v->min; v=u; /* the former parent of |v| is the new current vertex |v| */ } } @ The elements of the active stack are always in order by rank, and all children of a vertex~|v| in the tree have rank higher than~|v|. Tarjan's algorithm relies on a converse property: {\sl All active nodes whose rank exceeds that of the current vertex~|v| are descendants of~|v|.} (This property holds because the algorithm has constructed the tree by assigning ranks in preorder, ``the order of succession to the throne.'' First come |v|'s firstborn and descendants, then the nextborn, and so on.) Therefore the descendants of the current vertex always appear consecutively at the top of the stack. Another fundamental property of Tarjan's algorithm is more subtle: {\sl There is always a way to get from any active vertex to the current vertex.} This follows from the fact that all mature active vertices~|u| have |u->min->rank<u->rank|. If some active vertex does not lead to the current vertex~|v|, let |u| be the counterexample with smallest rank. Then |u| isn't an ancestor of~|v|, hence |u| must be mature; hence it leads to the active vertex |u->min|, from which there {\sl is\/} a path to~|v|, contradicting our assumption. Therefore |v| and its active descendants are all reachable from each other, and they must belong to the same strong component. Moreover, if |v->min=v|, this component can't be made any larger. For there is no arc from any of these vertices to an unseen vertex; all arcs from |v| and its descendants have already been tagged. And there is no arc from any of these vertices to an active vertex that is below |v| on the stack; otherwise |v->min| would have smaller rank than~|v|. Hence all arcs, if any, that lead from these vertices to some other vertex must lead to settled vertices. And we know from previous steps of the computation that the settled vertices all belong to other strong components. Therefore we are justified in settling |v| and its active descendants now. Removing them from the tree of active vertices does not remove any vertex from which there is a path to a vertex of rank less than |v->rank|. Hence their removal does not affect the validity of the |u->min| value for any vertex~|u| that remains active. We print out enough information for a reader to verify the strength of the claimed component easily. @d infinity g->n /* infinite rank (or close enough) */ @<Remove |v| and all its successors on the active stack from the tree, and mark them as a strong component of the graph@>= {@+register Vertex *t; /* runs through the vertices of the new strong component */ t=active_stack; active_stack=v->link; v->link=settled_stack; settled_stack=t; /* we've moved the top of one stack to the other */ printf("Strong component `%ld %s'", specs(v)); if (t==v) putchar('\n'); /* single vertex */ else { printf(" also includes:\n"); while (t!=v) { printf(" %ld %s (from %ld %s; ..to %ld %s)\n", specs(t), specs(t->parent), specs(t->min)); t->rank=infinity; /* now |t| is settled */ t->parent=v; /* and |v| represents the new strong component */ t=t->link; } } v->rank=infinity; /* |v| too is settled */ v->parent=v; /* and represents its own strong component */ } @ After all the strong components have been found, we can also compute the relations between them, without mentioning any cross-connection more than once. In fact, we built the |settled_stack| precisely so that this task could be done easily without sorting or searching. This part of the algorithm wouldn't be necessary if we were interested only in the strong components themselves. For this step we use the name |arc_from| for the field we previously called |untagged|. The trick here relies on the fact that all vertices of the same strong component appear together in |settled_stack|. @d arc_from x.V /* utility field |x| will now point to a vertex */ @<Print out one representative of each arc that runs between...@>= printf("\nLinks between components:\n"); for (v=settled_stack; v; v=v->link) {@+register Vertex *u=v->parent; register Arc *a; u->arc_from=u; for (a=v->arcs; a; a=a->next) {@+register Vertex *w=a->tip->parent; if (w->arc_from!=u) { w->arc_from=u; printf("%ld %s -> %ld %s (e.g., %ld %s -> %ld %s)\n", specs(u),specs(w),specs(v),specs(a->tip)); } } } @* Index. We close with a list that shows where the identifiers of this program are defined and used. ����������������������������������������������������������������������������������������������������������������take_risc.w�����������������������������������������������������������������������������������������0000444�0001750�0001750�00000015230�05440404574�011341� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{TAKE\_\,RISC} \prerequisite{GB\_\,GATES} @* Introduction. This demonstration program uses graphs constructed by the |risc| procedure in the {\sc GB\_\,GATES} module to produce an interactive program called \.{take\_risc}, which multiplies and divides small numbers the slow way---by simulating the behavior of a logical circuit, one gate at a time. The program assumes that \UNIX/ conventions are being used. Some code in sections listed under `\UNIX/ dependencies' in the index might need to change if this program is ported to other operating systems. \def\<#1>{$\langle${\rm#1}$\rangle$} To run the program under \UNIX/, say `\.{take\_risc} \<trace>', where \<trace> is nonempty if and only if you want the machine computations to be printed out. The program will prompt you for two numbers, and it will use the simulated RISC machine to compute their product and quotient. Then it will ask for two more numbers, and so on. @ Here is the general layout of this program, as seen by the \CEE/ compiler: @^UNIX dependencies@> @p #include "gb_graph.h" /* the standard GraphBase data structures */ #include "gb_gates.h" /* routines for gate graphs */ @h@# @<Global variables@>@; main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ { trace=(argc>1? 8: 0); /* we'll show registers 0--7 if tracing */ if ((g=risc(8L))==NULL) { printf("Sorry, I couldn't generate the graph (trouble code %ld)!\n", panic_code); return(-1); } printf("Welcome to the world of microRISC.\n"); while(1) { @<Prompt for two numbers; |break| if unsuccessful@>; @<Use the RISC machine to compute the product, |p|@>; printf("The product of %ld and %ld is %ld%s.\n",m,n,p, o?" (overflow occurred)":""); @<Use the RISC machine to compute the quotient and remainder, |q| and~|r|@>; printf("The quotient is %ld, and the remainder is %ld.\n",q,r); } return 0; /* normal exit */ } @ @<Glob...@>= Graph *g; /* graph that defines a simple RISC machine */ long o,p,q,r; /* overflow, product, quotient, remainder */ long trace; /* number of registers to trace */ long m,n; /* numbers to be multiplied and divided */ char buffer[100]; /* input buffer */ @ @d prompt(s) {@+printf(s);@+fflush(stdout); /* make sure the user sees the prompt */ if (fgets(buffer,99,stdin)==NULL) break;@+} @<Prompt...@>= prompt("\nGimme a number: "); step0:if (sscanf(buffer,"%ld",&m)!=1) break; step1:if (m<=0) { prompt("Excuse me, I meant a positive number: "); if (sscanf(buffer,"%ld",&m)!=1) break; if (m<=0) break; } while (m>0x7fff) { prompt("That number's too big; please try again: "); if (sscanf(buffer,"%ld",&m)!=1) goto step0; /* |step0| will |break| out */ if (m<=0) goto step1; } @<Now do the same thing for |n| instead of |m|@>; @ @<Now do the same thing for |n| instead of |m|@>= prompt("OK, now gimme another: "); if (sscanf(buffer,"%ld",&n)!=1) break; step2:if (n<=0) { prompt("Excuse me, I meant a positive number: "); if (sscanf(buffer,"%ld",&n)!=1) break; if (n<=0) break; } while (n>0x7fff) { prompt("That number's too big; please try again: "); if (sscanf(buffer,"%ld",&n)!=1) goto step0; /* |step0| will |break| out */ if (n<=0) goto step2; } @* A RISC program. Here is the little program we will run on the little computer. It consists mainly of a subroutine called |tri|, which computes the value of the ternary operation $x\lfloor y/z\rfloor$, assuming that $y\ge0$ and $z>0$; the inputs $x,y,z$ appear in registers $1,2,3$, respectively, and the exit address is assumed to be in register~7. As special cases we can compute the product $xy$ (letting $z=1$) or the quotient $\lfloor y/z\rfloor$ (letting $x=1$). When the subroutine returns, it leaves the result in register~4; it also leaves the value $(y\bmod z)-z$ in register~2. Overflow will be set if and only if the true result was not between $-2^{15}$ and $2^{15}-1$, inclusive. It would not be difficult to modify the code to make it work with unsigned 16-bit numbers, or to make it deliver results with 32 or 48 or perhaps even 64 bits of precision. @d div 7 /* location `|div|' in the program below */ @d mult 10 /* location `|mult|' in the program below */ @d memry_size 34 /* the number of instructions in the program below */ @<Glob...@>= unsigned long memry[memry_size]={ /* a ``read-only memory'' used by |run_risc| */ 0x2ff0, /* |start:| $\\{r2}=m$ (contents of next word) */ 0x1111, /* (we will put the value of |m| here, in |memry[1]|) */ 0x1a30, /* \quad$\\{r1}=n$ (contents of next word) */ 0x3333, /* (we will put the value of |n| here, in |memry[3]|) */ 0x7f70, /* \quad\&{jumpto} (contents of next word), $\\{r7}={}$return address */ 0x5555, /* (we will put either |mult| or |div| here, in |memry[5]|) */ 0x0f8f, /* halt without changing any status bits */ 0x3a21, /* |div:| $\\{r3}=\\{r1}$ */ 0x1a01, /* \quad$\\{r1}=1$ */ 0x0a12, /* \quad|goto tri| (literally, |@t\\{r0}@>+=2|) */ 0x3a01, /* |mult:| $\\{r3}=1$ */ 0x4000, /* |tri:| $\\{r4}=0$ */ 0x5000, /* \quad$\\{r5}=0$ */ 0x6000, /* \quad$\\{r6}=0$ */ 0x2a63, /* \quad|@t\\{r2}@>-=@t\\{r3}@>| */ 0x0f95, /* \quad|goto l2| */ 0x3063, /* |l1:| |@t\\{r3}@><<=1| */ 0x1061, /* \quad|@t\\{r1}@><<=1| */ 0x6ac1, /* \quad|if| (overflow) $\\{r6}=1$ */ 0x5fd1, /* \quad|@t\\{r5}@>++| */ 0x2a63, /* |l2:| |@t\\{r2}@>-=@t\\{r3}@>| */ 0x039b, /* \quad|if| ($\ge0$) |goto l1| */ 0x0843, /* \quad|goto l4| */ 0x3463, /* |l3:| |@t\\{r3}@>>>=1| */ 0x1561, /* \quad|@t\\{r1}@>>>=1| */ 0x2863, /* |l4:| |@t\\{r2}@>+=@t\\{r3}@>| */ 0x0c94, /* \quad|if| ($<0$) |goto l5| */ 0x4861, /* \quad|@t\\{r4}@>+=@t\\{r1}@>| */ 0x6ac1, /* \quad|if| (overflow) $\\{r6}=1$ */ 0x2a63, /* \quad|@t\\{r2}@>-=@t\\{r3}@>| */ 0x5a41, /* |l5:| |@t\\{r5}@>--| */ 0x0398, /* \quad|if| ($\ge0$) |goto l3| */ 0x6666, /* \quad|if| (\\{r6}) force overflow (literally |@t\\{r6}@>>>=4|) */ 0x0fa7}; /* \quad|return| (literally, $\\{r0}=\\{r7}$, preserving overflow) */ @ @<Use the RISC machine to compute the product, |p|@>= memry[1]=m; memry[3]=n; memry[5]=mult; run_risc(g,memry,memry_size,trace); p=(long)risc_state[4]; o=(long)risc_state[16]&1; /* the overflow bit */ @ @<Use the RISC machine to compute the quotient and remainder, |q| and~|r|@>= memry[5]=div; run_risc(g,memry,memry_size,trace); q=(long)risc_state[4]; r=((long)(risc_state[2]+n))&0x7fff; @* Index. Finally, here's a list that shows where the identifiers of this program are defined and used. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������word_components.w�����������������������������������������������������������������������������������0000444�0001750�0001750�00000011115�05407715444�012617� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{WORD\_\,COMPONENTS} \prerequisite{GB\_WORDS} @* Components. \kern-.7pt This simple demonstration program computes the connected components of the GraphBase graph of five-letter words. It prints the words in order of decreasing weight, showing the number of edges, components, and isolated vertices present in the graph defined by the first $n$ words for all~$n$. @p #include "gb_graph.h" /* the GraphBase data structures */ #include "gb_words.h" /* the |words| routine */ @h@#@; main() {@+Graph *g=words(0L,0L,0L,0L); /* the graph we love */ Vertex *v; /* the current vertex being added to the component structure */ Arc *a; /* the current arc of interest */ long n=0; /* the number of vertices in the component structure */ long isol=0; /* the number of isolated vertices in the component structure */ long comp=0; /* the current number of components */ long m=0; /* the current number of edges */ printf("Component analysis of %s\n",g->id); for (v=g->vertices; v<g->vertices+g->n; v++) { n++, printf("%4ld: %5ld %s",n,v->weight,v->name); @<Add vertex |v| to the component structure, printing out any components it joins@>; printf("; c=%ld,i=%ld,m=%ld\n", comp, isol, m); } @<Display all unusual components@>; return 0; /* normal exit */ } @ The arcs from |v| to previous vertices all appear on the list |v->arcs| after the arcs from |v| to future vertices. In this program, we aren't interested in the future, only the past; so we skip the initial arcs. @<Add vertex |v| to the component structure...@>= @<Make |v| a component all by itself@>; a=v->arcs; while (a && a->tip>v) a=a->next; if (!a) printf("[1]"); /* indicate that this word is isolated */ else {@+long c=0; /* the number of merge steps performed because of |v| */ for (; a; a=a->next) {@+register Vertex *u=a->tip; m++; @<Merge the components of |u| and |v|, if they differ@>; } printf(" in %s[%ld]", v->master->name, v->master->size); /* show final component */ } @ We keep track of connected components by using circular lists, a procedure that is known to take average time $O(n)$ on truly random graphs [Knuth and Sch\"onhage, {\sl Theoretical Computer Science\/ @^Knuth, Donald Ervin@> @^Sch\"onhage, Arnold@> \bf 6} (1978), 281--315]. Namely, if |v| is a vertex, all the vertices in its component will be in the list $$\hbox{|v|, \ |v->link|, \ |v->link->link|, \ \dots,}$$ eventually returning to |v| again. There is also a master vertex in each component, |v->master|; if |v| is the master vertex, |v->size| will be the number of vertices in its component. @d link z.V /* link to next vertex in component (occupies utility field |z|) */ @d master y.V /* pointer to master vertex in component */ @d size x.I /* size of component, kept up to date for master vertices only */ @<Make |v| a component all by itself@>= v->link=v; v->master=v; v->size=1; isol++; comp++; @ When two components merge together, we change the identity of the master vertex in the smaller component. The master vertex representing |v| itself will change if |v| is adjacent to any prior vertex. @<Merge the components of |u| and |v|, if they differ@>= u=u->master; if (u!=v->master) {@+register Vertex *w=v->master, *t; if (u->size<w->size) { if (c++>0) printf("%s %s[%ld]", (c==2? " with": ","), u->name, u->size); w->size += u->size; if (u->size==1) isol--; for (t=u->link; t!=u; t=t->link) t->master=w; u->master=w; }@+else { if (c++>0) printf("%s %s[%ld]", (c==2? " with": ","), w->name, w->size); if (u->size==1) isol--; u->size += w->size; if (w->size==1) isol--; for (t=w->link; t!=w; t=t->link) t->master=u; w->master=u; } t=u->link; u->link=w->link; w->link=t; comp--; } @ The |words| graph has one giant component and lots of isolated vertices. We consider all other components unusual, so we print them out when the other computation is done. @<Display all unusual components@>= printf( "\nThe following non-isolated words didn't join the giant component:\n"); for (v=g->vertices; v<g->vertices+g->n; v++) if (v->master==v && v->size>1 && v->size+v->size<g->n) {@+register Vertex *u; long c=1; /* count of number printed on current line */ printf("%s", v->name); for (u=v->link; u!=v; u=u->link) { if (c++==12) putchar('\n'),c=1; printf(" %s",u->name); } putchar('\n'); } @* Index. We close with a list that shows where the identifiers of this program are defined and used. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������boilerplate.w���������������������������������������������������������������������������������������0000444�0001750�0001750�00000003435�05407367204�011704� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% This material goes at the beginning of all Stanford GraphBase CWEB files \def\topofcontents{ \leftline{\sc\today\ at \hours}\bigskip\bigskip \centerline{\titlefont\title}} \font\ninett=cmtt9 \def\botofcontents{\vskip 0pt plus 1filll \ninerm\baselineskip10pt \noindent\copyright\ 1993 Stanford University \bigskip\noindent This file may be freely copied and distributed, provided that no changes whatsoever are made. All users are asked to help keep the Stanford GraphBase files consistent and ``uncorrupted,'' identical everywhere in the world. Changes are permissible only if the modified file is given a new name, different from the names of existing files in the Stanford GraphBase, and only if the modified file is clearly identified as not being part of that GraphBase. (The {\ninett CWEB} system has a ``change file'' facility by which users can easily make minor alterations without modifying the master source files in any way. Everybody is supposed to use change files instead of changing the files.) The author has tried his best to produce correct and useful programs, in order to help promote computer science research, but no warranty of any kind should be assumed. \smallskip\noindent Preliminary work on the Stanford GraphBase project was supported in part by National Science Foundation grant CCR-86-10181.} \def\prerequisite#1{\def\startsection{\noindent Important: Before reading {\sc\title}, please read or at least skim the program for {\sc#1}.\bigskip \let\startsection=\stsec\stsec}} \def\prerequisites#1#2{\def\startsection{\noindent Important: Before reading {\sc\title}, please read or at least skim the programs for {\sc#1} and {\sc#2}.\bigskip \let\startsection=\stsec\stsec}} �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gb_dijk.w�������������������������������������������������������������������������������������������0000444�0001750�0001750�00000040654�10222173666�010775� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_\,DIJK} \prerequisite{GB\_\,GRAPH} @* Introduction. The GraphBase demonstration routine |dijkstra(uu,vv,gg,hh)| finds a shortest path from vertex~|uu| to vertex~|vv| in graph~|gg|, with the aid of an optional heuristic function~|hh|. This function implements a version of Dijkstra's algorithm, a general procedure for determining shortest paths in a directed graph that has nonnegative arc lengths [E.~W. Dijkstra, ``A note on two problems in connexion with graphs,'' {\sl Numerische Mathematik\/ \bf1} (1959), 269--271]. @^Dijkstra, Edsger Wybe@> If |hh| is null, the length of every arc in |gg| must be nonnegative. If |hh| is non-null, |hh| should be a function defined on the vertices of the graph such that the length |d| of an arc from |u| to~|v| always satisfies the condition $$ d \ge |hh|(u)-|hh|(v)\,. $$ In such a case, we can effectively replace each arc length |d| by |d-hh(u)+hh(v)|, obtaining a graph with nonnegative arc lengths. The shortest paths between vertices in this modified graph are the same as they were in the original graph. The basic idea of Dijkstra's algorithm is to explore the vertices of the graph in order of their distance from the starting vertex~|uu|, proceeding until |vv| is encountered. If the distances have been modified by a heuristic function |hh| such that |hh(u)| happens to equal the true distance from |u| to~|vv|, for all~|u|, then all of the modified distances on shortest paths to |vv| will be zero. This means that the algorithm will explore all of the most useful arcs first, without wandering off in unfruitful directions. In practice we usually don't know the exact distances to |vv| in advance, but we can often compute an approximate value |hh(u)| that will help focus the search. If the external variable |verbose| is nonzero, |dijkstra| will record its activities on the standard output file by printing the distances from |uu| to all vertices it visits. After |dijkstra| has found a shortest path, it returns the length of that path. If no path from |uu| to~|vv| exists (in particular, if |vv| is~|NULL|), it returns |-1|; in such a case, the shortest distances from |uu| to all vertices reachable from~|uu| will have been computed and stored in the graph. An auxiliary function, |print_dijkstra_result(vv)|, can be used to display the actual path found, if one exists. Examples of the use of |dijkstra| appear in the {\sc LADDERS} demonstration module. @ This \CEE/ module is meant to be loaded as part of another program. It has the following simple structure: @p #include "gb_graph.h" /* define the standard GraphBase data structures */ @h@# @<Priority queue procedures@>@; @<Global declarations@>@; @<The |dijkstra| procedure@>@; @<The |print_dijkstra_result| procedure@>@; @ Users of {\sc GB\_\,DIJK} should include the header file \.{gb\_dijk.h}: @(gb_dijk.h@>= extern long dijkstra(); /* procedure to calculate shortest paths */ #define print_dijkstra_result p_dijkstra_result /* shorthand for linker */ extern void print_dijkstra_result(); /* procedure to display the answer */ @* The main algorithm. As Dijkstra's algorithm proceeds, it ``knows'' shortest paths from |uu| to more and more vertices; we will call these vertices ``known.'' Initially only |uu| itself is known. The procedure terminates when |vv| becomes known, or when all vertices reachable from~|uu| are known. Dijkstra's algorithm looks at all vertices adjacent to known vertices. A vertex is said to have been ``seen'' if it is either known or adjacent to a vertex that's known. The algorithm proceeds by learning to know all vertices in a greater and greater radius from the starting point. Thus, if |v|~is a known vertex at distance~|d| from~|uu|, every vertex at distance less than~|d| from |uu| will also be known. (Throughout this discussion the word ``distance'' actually means ``distance modified by the heuristic function''; we omit mentioning the heuristic because we can assume that the algorithm is operating on a graph with modified distances.) The algorithm maintains an auxiliary list of all vertices that have been seen but aren't yet known. For every such vertex~|v|, it remembers the shortest distance~|d| from |uu| to~|v| by a path that passes entirely through known vertices except for the very last arc. This auxiliary list is actually a priority queue, ordered by the |d| values. If |v|~is a vertex of the priority queue having the smallest |d|, we can remove |v| from the queue and consider it known, because there cannot be a path of length less than~|d| from |uu| to~|v|. (This is where the assumption of nonnegative arc length is crucial to the algorithm's validity.) @ To implement the ideas just sketched, we use several of the utility fields in vertex records. Each vertex~|v| has a |dist| field |v->dist|, which represents its true distance from |uu| if |v| is known; otherwise |v->dist| represents the shortest distance from |uu| discovered so far. Each vertex |v| also has a |backlink| field |v->backlink|, which is non-|NULL| if and only if |v| has been seen. In that case |v->backlink| is a vertex one step ``closer'' to |uu|, on a path from |uu| to |v| that achieves the current distance |v->dist|. (Exception: Vertex~|uu| has a backlink pointing to itself.) The backlink fields thereby allow us to construct shortest paths from |uu| to all the known vertices, if desired. @d dist z.I /* distance from |uu|, modified by |hh|, appears in vertex utility field |z| */ @d backlink y.V /* pointer to previous vertex appears in utility field |y| */ @(gb_dijk.h@>= #define dist @[z.I@] #define backlink @[y.V@] @ The priority queue is implemented by four procedures: \begingroup \def\]#1 {\smallskip\hangindent2\parindent \hangafter1 \indent #1 } \]|init_queue(d)| makes the queue empty and prepares for subsequent keys |>=d|. \]|enqueue(v,d)| puts vertex |v| in the queue and assigns it the key value |v->dist=d|. \]|requeue(v,d)| takes vertex |v| out of the queue and enters it again with the smaller key value |v->dist=d|. \]|del_min()| removes a vertex with minimum key from the queue and returns a pointer to that vertex. If the queue is empty, |NULL| is returned. \endgroup\smallskip\noindent These procedures are accessed via external pointers, so that the user of {\sc GB\_\,DIJK} can supply alternate queueing methods if desired. @(gb_dijk.h@>= extern void @[@] (*init_queue)(); /* create an empty priority queue for |dijkstra| */ extern void @[@] (*enqueue)(); /* insert a new element in the priority queue */ extern void @[@] (*requeue)(); /* decrease the key of an element in the queue */ extern Vertex *(*del_min)(); /* remove an element with smallest key */ @ The heuristic function might take a while to compute, so we avoid recomputation by storing |hh(v)| in another utility field |v->hh_val| once we've evaluated it. @d hh_val x.I /* computed value of |hh(v)| */ @(gb_dijk.h@>= #define hh_val @[x.I@] @ If no heuristic function is supplied by the user, we replace it by a dummy function that simply returns 0 in all cases. @<Global...@>= static long dummy(v) Vertex *v; {@+return 0;@+} @ Here now is |dijkstra|: @<The |dijkstra| procedure@>= long dijkstra(uu,vv,gg,hh) Vertex *uu; /* the starting point */ Vertex *vv; /* the ending point */ Graph *gg; /* the graph they belong to */ long @[@] (*hh)(); /* heuristic function */ {@+register Vertex *t; /* current vertex of interest */ if (!hh) hh=dummy; /* change to default heuristic */ @<Make |uu| the only vertex seen; also make it known@>; t=uu; if (verbose) @<Print initial message@>; while (t!=vv) { @<Put all unseen vertices adjacent to |t| into the queue, and update the distances of other vertices adjacent to~|t|@>; t=(*del_min)(); if (t==NULL) return -1; /* if the queue becomes empty, there's no way to get to |vv| */ if (verbose) @<Print the distance to |t|@>; } return vv->dist-vv->hh_val+uu->hh_val; /* true distance from |uu| to |vv| */ } @ As stated above, a vertex is considered seen only when its backlink isn't null, and known only when it is seen but not in the queue. @<Make |uu| the only...@>= for (t=gg->vertices+gg->n-1; t>=gg->vertices; t--) t->backlink=NULL; uu->backlink=uu; uu->dist=0; uu->hh_val=(*hh)(uu); (*init_queue)(0L); /* make the priority queue empty */ @ Here we help the \CEE/ compiler in case it hasn't got a great optimizer. @<Put all unseen vertices adjacent to |t| into the queue...@>= {@+register Arc *a; /* an arc leading from |t| */ register long d = t->dist - t->hh_val; for (a=t->arcs; a; a=a->next) { register Vertex *v = a->tip; /* a vertex adjacent to |t| */ if (v->backlink) { /* |v| has already been seen */ register long dd = d + a->len + v->hh_val; if (dd< v->dist) { v->backlink = t; (*requeue)(v,dd); /* we found a better way to get there */ } }@+else { /* |v| hasn't been seen before */ v->hh_val = (*hh)(v); v->backlink = t; (*enqueue)(v, d + a->len + v->hh_val); } } } @ The |dist| fields don't contain true distances in the graph; they represent distances modified by the heuristic function. The true distance from |uu| to vertex |v| is |v->dist - v->hh_val + uu->hh_val|. When printing the results, we show true distances. Also, if a nontrivial heuristic is being used, we give the |hh| value in brackets; the user can then observe that vertices are becoming known in order of true distance plus |hh| value. @<Print initial message@>= {@+printf("Distances from %s", uu->name); if (hh!=dummy) printf(" [%ld]", uu->hh_val); printf(":\n"); } @ @<Print the distance to |t|@>= {@+printf(" %ld to %s", t->dist - t->hh_val + uu->hh_val, t->name); if (hh!=dummy) printf(" [%ld]", t->hh_val); printf(" via %s\n", t->backlink->name); } @ After |dijkstra| has found a shortest path, the backlinks from~|vv| specify the steps of that path. We want to print the path in the forward direction, so we reverse the links. We also unreverse them again, just in case the user didn't want the backlinks to be trashed. Indeed, this procedure can be used for any vertex |vv| whose backlink is non-null, not only the |vv| that was a parameter to |dijkstra|. List reversal is conveniently regarded as a process of popping off one stack and pushing onto another. @d print_dijkstra_result p_dijkstra_result /* shorthand for linker */ @<The |print_dijkstra_result| procedure@>= void print_dijkstra_result(vv) Vertex *vv; /* ending vertex */ {@+register Vertex *t, *p, *q; /* registers for reversing links */ t=NULL, p=vv; if (!p->backlink) { printf("Sorry, %s is unreachable.\n",p->name); return; } do@+{ /* pop an item from |p| to |t| */ q=p->backlink; p->backlink=t; t=p; p=q; }@+while (t!=p); /* the loop stops with |t==p==uu| */ do@+{ printf("%10ld %s\n", t->dist-t->hh_val+p->hh_val, t->name); t=t->backlink; }@+while (t); t=p; do@+{ /* pop an item from |t| to |p| */ q=t->backlink; t->backlink=p; p=t; t=q; }@+while (p!=vv); } @* Priority queues. Here we provide a simple doubly linked list for queueing; this is a convenient default, good enough for applications that aren't too large. (See {\sc MILES\_\,SPAN} for implementations of other schemes that are more efficient when the queue gets large.) The two queue links occupy two of a vertex's remaining utility fields. @d llink v.V /* |llink| is stored in utility field |v| of a vertex */ @d rlink w.V /* |rlink| is stored in utility field |w| of a vertex */ @<Glob...@>= void @[@] (*init_queue)() = init_dlist; /* create an empty dlist */ void @[@] (*enqueue)() = enlist; /* insert a new element in dlist */ void @[@] (*requeue)() = reenlist ; /* decrease the key of an element in dlist */ Vertex *(*del_min)() = del_first; /* remove element with smallest key */ @ There's a special list head, from which we get to everything else in the queue in decreasing order of keys by following |llink| fields. The following declaration actually provides for 128 list heads. Only the first of these is used here, but we'll find something to do with the other 127 later. @<Prior...@>= static Vertex head[128]; /* list-head elements that are always present */ @# void init_dlist(d) long d; { head->llink=head->rlink=head; head->dist=d-1; /* a value guaranteed to be smaller than any actual key */ } @ It seems reasonable to assume that an element entering the queue for the first time will tend to have a larger key than the other elements. Indeed, in the special case that all arcs in the graph have the same length, this strategy turns out to be quite fast. For in that case, every vertex is added to the end of the queue and deleted from the front, without any requeueing; the algorithm produces a strict first-in-first-out queueing discipline and performs a breadth-first search. @<Prior...@>= void enlist(v,d) Vertex *v; long d; {@+register Vertex *t=head->llink; v->dist=d; while (d<t->dist) t=t->llink; v->llink=t; (v->rlink=t->rlink)->llink=v; t->rlink=v; } @ @<Prior...@>= void reenlist(v,d) Vertex *v; long d; {@+register Vertex *t=v->llink; (t->rlink=v->rlink)->llink=v->llink; /* remove |v| */ v->dist=d; /* we assume that the new |dist| is smaller than it was before */ while (d<t->dist) t=t->llink; v->llink=t; (v->rlink=t->rlink)->llink=v; t->rlink=v; } @ @<Prior...@>= Vertex *del_first() {@+Vertex *t; t=head->rlink; if (t==head) return NULL; (head->rlink=t->rlink)->llink=head; return t; } @* A special case. When the arc lengths in the graph are all fairly small, we can substitute another queueing discipline that does each operation quickly. Suppose the only lengths are 0, 1, \dots,~|k-1|; then we can prove easily that the priority queue will never contain more than |k| different values at once. Moreover, we can implement it by maintaining |k| doubly linked lists, one for each key value mod~|k|. For example, let |k=128|. Here is an alternate set of queue commands, to be used when the arc lengths are known to be less than~128. @ @<Prior...@>= static long master_key; /* smallest key that may be present in the priority queue */ @# void init_128(d) long d; {@+register Vertex *u; master_key=d; for (u=head; u<head+128; u++) u->llink=u->rlink=u; } @ If the number of lists were not a power of 2, we would calculate a remainder by division instead of by bitwise-anding. @<Prior...@>= Vertex *del_128() {@+long d; register Vertex *u, *t; for (d=master_key; d<master_key+128; d++) { u=head+(d&0x7f); /* that's |d%128| */ t=u->rlink; if (t!=u) { /* we found a nonempty list with minimum key */ master_key=d; (u->rlink = t->rlink)->llink = u; return t; /* incidentally, |t->dist = d| */ } } return NULL; /* all 128 lists are empty */ } @ @<Prior...@>= void enq_128(v,d) Vertex *v; /* new vertex for the queue */ long d; /* its |dist| */ {@+register Vertex *u=head+(d&0x7f); v->dist = d; (v->llink = u->llink)->rlink = v; v->rlink = u; u->llink = v; } @ All of these operations have been so simple, one wonders why the lists should be doubly linked. Single linking would indeed be plenty---if we didn't have to support the |requeue| operation. But requeueing involves deleting an arbitrary element from the middle of its list. And we do seem to need two links for that. In the application to Dijkstra's algorithm, the new |d| will always be |master_key| or more. But we want to implement requeueing in general, so that this procedure can be used also for other algorithms such as the calculation of minimum spanning trees (see {\sc MILES\_\,SPAN}). @<Prior...@>= void req_128(v,d) Vertex *v; /* vertex to be moved to another list */ long d; /* its new |dist| */ {@+register Vertex *u=head+(d&0x7f); (v->llink->rlink=v->rlink)->llink=v->llink; /* remove |v| */ v->dist=d; /* the new |dist| is smaller than it was before */ (v->llink=u->llink)->rlink = v; v->rlink = u; u->llink = v; if (d<master_key) master_key=d; /* not needed for Dijkstra's algorithm */ } @ The user of {\sc GB\_\,DIJK} needs to know the names of these queueing procedures if changes to the defaults are made, so we'd better put the necessary info into the header file. @(gb_dijk.h@>= extern void init_dlist(); extern void enlist(); extern void reenlist(); extern Vertex *del_first(); extern void init_128(); extern Vertex *del_128(); extern void enq_128(); extern void req_128(); @* Index. Here is a list that shows where the identifiers of this program are defined and used. ������������������������������������������������������������������������������������gb_save.w�������������������������������������������������������������������������������������������0000444�0001750�0001750�00000101416�06256067211�011004� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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\_\,SAVE} \prerequisites{GB\_\,GRAPH}{GB\_\,IO} @* Introduction. This GraphBase module contains the code for two special utility routines, |save_graph| and |restore_graph|, which convert graphs back and forth between the internal representation that is described in {\sc GB\_\,GRAPH} and a symbolic file format that is described below. Researchers can use these routines to transmit graphs between computers in a machine-independent way, or to use GraphBase graphs with other graph manipulation software that supports the same symbolic format. All kinds of tricks are possible in the \CEE/ language, so it is easy to abuse the GraphBase conventions and to create data structures that make sense only on a particular machine. But if users follow the recommended ground rules, |save_graph| will be able to transform their graphs into files that any other GraphBase installation will be able to read with |restore_graph|. The graphs created on remote machines will then be semantically equivalent to the originals. Restrictions: Strings must contain only standard printable characters, not including \.\\ or \." or newline, and must be at most 4095 characters long; the |g->id| string should be at most 154 characters long. All pointers to vertices and arcs must be confined to blocks within the |g->data| area; blocks within |g->aux_data| are not saved or restored. Storage blocks in |g->data| must be ``pure''; that is, each block must be entirely devoted either to |Vertex| records, or to |Arc| records, or to characters of strings. The |save_graph| procedure places all |Vertex| records into a single |Vertex| block and all |Arc| records into a single |Arc| block, preserving the relative order of the original records where possible; but it does not preserve the relative order of string data in memory. For example, if |u->name| and |v->name| point to the same memory location in the saved graph, they will point to different memory locations (representing equal strings) in the restored graph. All utility fields must conform to the conventions of the graph's |util_types| string; the \.G option, which leads to graphs within graphs, is not permitted in that string. @d MAX_SV_STRING 4095 /* longest strings supported */ @d MAX_SV_ID 154 /* longest |id| supported, is less than |ID_FIELD_SIZE| */ @(gb_save.h@>= extern long save_graph(); extern Graph *restore_graph(); @ Here is an overview of the \CEE/ code, \.{gb\_save.c}, for this module: @p #include "gb_io.h" /* we use the input/output conventions of {\sc GB\_\,IO} */ #include "gb_graph.h" /* and, of course, the data structures of {\sc GB\_\,GRAPH} */ @h@# @<Type declarations@>@; @<Private variables@>@; @<Private functions@>@; @<External functions@> @* External representation of graphs. The internal representation of graphs has been described in {\sc GB\_\,GRAPH}. We now need to supplement that description by devising an alternative format suitable for human-and-machine-readable files. The following somewhat contrived example illustrates the simple conventions that we shall follow: $$\let\par=\cr \obeylines % \vbox{\halign{\.{#}\hfil * GraphBase graph (util\_types IZAZZZZVZZZZSZ,3V,4A) "somewhat\_contrived\_example(3.14159265358979323846264338327\\ 9502884197169399375105820974944592307816406286208998628)",1, 3,"pi" * Vertices "look",A0,15,A1 "feel",0,-9,A1 "",0,0,0 * Arcs V0,A2,3,V1 V1,0,5,0 V1,0,-8,1 0,0,0,0 * Checksum 271828 }}$$ The first line specifies the 14 characters of |util_types| and the total number of |Vertex| and |Arc| records; in this case there are 3 vertices and 4~arcs. The next line or lines specify the |id|, |n|, and |m| fields of the |Graph| record, together with any utility fields that are not being ignored. In this case, the |id| is a rather long string; a string may be broken into parts by ending the initial parts with a backslash, so that no line of the file has more than 79 characters. The last six characters of |util_types| refer to the utility fields of the |Graph| record, and in this case they are \.{ZZZZSZ}; so all utility fields are ignored except the second-to-last, |yy|, which is of type string. The |restore_graph| routine will construct a |Graph| record~|g| from this example in which |g->n=1|, |g->m=3|, and |g->yy.S="pi"|. Notice that the individual field values for a record are separated by commas. If a line ends with a comma, the following line contains additional fields of the same record. After the |Graph| record fields have been specified, there's a special line `\.{*\ Vertices}', after which we learn the fields of each vertex in turn. First comes the |name| field, then the |arcs| field, and then any non-ignored utility fields. In this example the |util_types| for |Vertex| records are \.{IZAZZZ}, so the utility field values are |u.I| and |w.A|. Let |v| point to the first |Vertex| record (which incidentally is also pointed to by |g->vertices|), and let |a| point to the first |Arc| record. Then in this example we will have |v->name="look"|, |v->arcs=a|, |v->u.I=15|, and |v->w.A=(a+1)|. After the |Vertex| records comes a special line `\.{*\ Arcs}', followed by the fields of each |Arc| record in an entirely analogous way. First comes the |tip| field, then the |next| field, then the |len|, and finally the utility fields (if any). In this example the |util_types| for |Arc| utility fields are \.{ZV}; hence field |a| is ignored, and field~|b| is a pointer to a |Vertex|. We will have |a->tip=v|, |a->next=(a+2)|, |a->len=3|, and |a->b.V=(v+1)|. The null pointer |NULL| is denoted by \.0. Furthermore, a |Vertex| pointer is allowed to have the special value \.1, because of conventions explained in {\sc GB\_\,GATES}. (This special value appears in the fourth field of the third arc in the example above.) The |restore_graph| procedure does not allow |Vertex| pointers to take on constant values greater than~1, nor does it permit the value `\.1' where an |Arc| pointer ought to be. There should be exactly as many |Vertex| and |Arc| specifications as indicated after the utility types at the beginning of the file. The final |Arc| should then be followed by a special checksum line, which must contain either a number consistent with the data on all the previous lines or a negative value (which is not checked). All information after the checksum line is ignored. Users should not edit the files produced by |save_graph|, because an incorrect checksum is liable to ruin everything. However, additional lines beginning with `\.*' may be placed as comments at the very beginning of the file; such lines are immune to checksumming. @ We can establish these conventions firmly in mind by writing the |restore_graph| routine before we write |save_graph|. The subroutine call |restore_graph("foo.gb")| produces a pointer to the graph defined in file |"foo.gb"|, or a null pointer in case that file is unreadable or incorrect. In the latter case, |panic_code| indicates the problem. @<External functions@>= Graph *restore_graph(f) char *f; /* the file name */ {@+Graph *g=NULL; /* the graph being restored */ register char *p; /* register for string manipulation */ long m; /* the number of |Arc| records to allocate */ long n; /* the number of |Vertex| records to allocate */ @<Open the file and parse the first line; |goto sorry| if there's trouble@>; @<Create the |Graph| record |g| and fill in its fields@>; @<Fill in the fields of all |Vertex| records@>; @<Fill in the fields of all |Arc| records@>; @<Check the checksum and close the file@>; return g; sorry: gb_raw_close();@+gb_recycle(g);@+return NULL; } @ As mentioned above, users can add comment lines at the beginning of the file, if they put a \.* at the beginning of every such line. But the line that precedes the data proper must adhere to strict standards. @d panic(c)@+{@+panic_code=c;@+goto sorry;@+} @<Open the file...@>= gb_raw_open(f); if (io_errors) panic(early_data_fault); /* can't open the file */ while (1) { gb_string(str_buf,')'); if (sscanf(str_buf,"* GraphBase graph (util_types %14[ZIVSA],%ldV,%ldA", str_buf+80,&n,&m)==3 && strlen(str_buf+80)==14) break; if (str_buf[0]!='*') panic(syntax_error); /* first line is unreadable */ } @ The previous code has placed the graph's |util_types| into location |str_buf+80| and verified that it contains precisely 14 characters, all belonging to the set $\{\.Z,\.I,\.V,\.S,\.A\}$. @<Create the |Graph| record |g| and fill in its fields@>= g=gb_new_graph(0L); if (g==NULL) panic(no_room); /* out of memory before we're even started */ gb_free(g->data); g->vertices=verts=gb_typed_alloc(n==0?1:n,Vertex,g->data); last_vert=verts+n; arcs=gb_typed_alloc(m==0?1:m,Arc,g->data); last_arc=arcs+m; if (gb_trouble_code) panic(no_room+1); /* not enough room for vertices and arcs */ strcpy(g->util_types,str_buf+80); gb_newline(); if (gb_char()!='"') panic(syntax_error+1); /* missing quotes before graph |id| string */ p=gb_string(g->id,'"'); if (*(p-2)=='\n' && *(p-3)=='\\' && p>g->id+2) { gb_newline(); gb_string(p-3,'"'); } if (gb_char()!='"') panic(syntax_error+2); /* missing quotes after graph |id| string */ @<Fill in |g->n|, |g->m|, and |g|'s utility fields@>; @ The |util_types| and |id| fields are slightly different from other string fields, because we store them directly in the |Graph| record instead of storing a pointer. The other fields to be filled by |restore_graph| can all be done by a macro called |fillin|, which invokes a subroutine called |fill_field|. The first parameter to |fillin| is the address of a field in a record; the second parameter is one of the codes $\{\.Z,\.I,\.V,\.S,\.A\}$. A global variable |comma_expected| is nonzero when this field is not the first in its record. The value returned by |fill_field| is nonzero if something goes wrong. We assume here that a utility field takes exactly as much space as a field of any of its constituent types. @^system dependencies@> @d fillin(l,t) if (fill_field((util*)&(l),t)) goto sorry @<Private f...@>= static long fill_field(l,t) util *l; /* location of field to be filled in */ char t; /* its type code */ {@+register char c; /* character just read */ if (t!='Z'&&comma_expected) { if (gb_char()!=',') return (panic_code=syntax_error-1); /* missing comma */ if (gb_char()=='\n') gb_newline(); else gb_backup(); } else comma_expected=1; c=gb_char(); switch (t) { case 'I': @<Fill in a numeric field@>; case 'V': @<Fill in a vertex pointer@>; case 'S': @<Fill in a string pointer@>; case 'A': @<Fill in an arc pointer @>; default: gb_backup();@+break; } return panic_code; } @ Some of the communication between |restore_graph| and |fillin| is best done via global variables. @<Private v...@>= static long comma_expected; /* should |fillin| look for a comma? */ static Vertex *verts; /* beginning of the block of |Vertex| records */ static Vertex *last_vert; /* end of the block of |Vertex| records */ static Arc *arcs; /* beginning of the block of |Arc| records */ static Arc *last_arc; /* end of the block of |Arc| records */ @ @<Fill in a numeric field@>= if (c=='-') l->I=-gb_number(10); else { gb_backup(); l->I=gb_number(10); } break; @ @<Fill in a vertex pointer@>= if (c=='V') { l->V=verts+gb_number(10); if (l->V>=last_vert || l->V<verts) panic_code=syntax_error-2; /* vertex address too big */ }@+else if (c=='0' || c=='1') l->I=c-'0'; else panic_code=syntax_error-3; /* vertex numeric address illegal */ break; @ @<Fill in an arc pointer@>= if (c=='A') { l->A=arcs+gb_number(10); if (l->A>=last_arc || l->A<arcs) panic_code=syntax_error-4; /* arc address too big */ }@+else if (c=='0') l->A=NULL; else panic_code=syntax_error-5; /* arc numeric address illegal */ break; @ We can restore a string slightly longer than the strings we can save. @<Fill in a string pointer@>= if (c!='"') panic_code=syntax_error-6; /* missing quotes at beginning of string */ else {@+register char* p; p=gb_string(item_buf,'"'); while (*(p-2)=='\n' && *(p-3)=='\\' && p>item_buf+2 && p<=buffer) { gb_newline(); p=gb_string(p-3,'"'); /* splice a broken string together */ } if (gb_char()!='"') panic_code=syntax_error-7; /* missing quotes at end of string */ else if (item_buf[0]=='\0') l->S=null_string; else l->S=gb_save_string(item_buf); } break; @ @d buffer (&item_buf[MAX_SV_STRING+3]) /* the last 81 chars of |item_buf| */ @<Private v...@>= static char item_buf[MAX_SV_STRING+3+81]; /* an item to be output */ @ When all fields of a record have been filled in, we call |finish_record| and hope that it returns~0. @<Private f...@>= static long finish_record() { if (gb_char()!='\n') return (panic_code=syntax_error-8); /* garbage present */ gb_newline(); comma_expected=0; return 0; } @ @<Fill in |g->n|, |g->m|, and |g|'s utility fields@>= panic_code=0; comma_expected=1; fillin(g->n,'I'); fillin(g->m,'I'); fillin(g->uu,g->util_types[8]); fillin(g->vv,g->util_types[9]); fillin(g->ww,g->util_types[10]); fillin(g->xx,g->util_types[11]); fillin(g->yy,g->util_types[12]); fillin(g->zz,g->util_types[13]); if (finish_record()) goto sorry; @ The rest is easy. @<Fill in the fields of all |Vertex| records@>= {@+register Vertex* v; gb_string(str_buf,'\n'); if (strcmp(str_buf,"* Vertices")!=0)@/ panic(syntax_error+3); /* introductory line for vertices is missing */ gb_newline(); for (v=verts;v<last_vert;v++) { fillin(v->name,'S'); fillin(v->arcs,'A'); fillin(v->u,g->util_types[0]); fillin(v->v,g->util_types[1]); fillin(v->w,g->util_types[2]); fillin(v->x,g->util_types[3]); fillin(v->y,g->util_types[4]); fillin(v->z,g->util_types[5]); if (finish_record()) goto sorry; } } @ @<Fill in the fields of all |Arc| records@>= {@+register Arc* a; gb_string(str_buf,'\n'); if (strcmp(str_buf,"* Arcs")!=0) panic(syntax_error+4); /* introductory line for arcs is missing */ gb_newline(); for (a=arcs;a<last_arc;a++) { fillin(a->tip,'V'); fillin(a->next,'A'); fillin(a->len,'I'); fillin(a->a,g->util_types[6]); fillin(a->b,g->util_types[7]); if (finish_record()) goto sorry; } } @ @<Check the checksum and close the file@>= {@+long s; gb_string(str_buf,'\n'); if (sscanf(str_buf,"* Checksum %ld",&s)!=1) panic(syntax_error+5); /* checksum line is missing */ if (gb_raw_close()!=s && s>=0) panic(late_data_fault); /* checksum does not match */ } @* Saving a graph. Now that we know how to restore a graph, once it has been saved, we are ready to write the |save_graph| routine. Users say |save_graph(g,"foo.gb")|; our job is to create a file |"foo.gb"| from which the subroutine call |restore_graph("foo.gb")| will be able to reconstruct a graph equivalent to~|g|, assuming that |g| meets the restrictions stated earlier. If nothing goes wrong, |save_graph| should return the value zero. Otherwise it should return an encoded trouble report. We will set things up so that |save_graph| produces a syntactically correct file |"foo.gb"| in almost every case, with explicit error indications written at the end of the file whenever certain aspects of the given graph have had to be changed. The value |-1| will be returned if |g==NULL|; the value |-2| will be returned if |g!=NULL| but the file |"foo.gb"| could not be opened for output; the value |-3| will be returned if memory is exhausted. In other cases a file |"foo.gb"| will be created. Here is a list of things that might go wrong, and the corresponding corrective actions to be taken in each case, assuming that |save_graph| does create a file: @d bad_type_code 0x1 /* illegal character, is changed to |'Z'| */ @d string_too_long 0x2 /* extralong string, is truncated */ @d addr_not_in_data_area 0x4 /* address out of range, is changed to |NULL| */ @d addr_in_mixed_block 0x8 /* address not in pure block, is |NULL|ified */ @d bad_string_char 0x10 /* illegal string character, is changed to |'?'| */ @d ignored_data 0x20 /* nonzero value in |'Z'| format, is not output */ @<Private v...@>= static long anomalies; /* problems accumulated by |save_graph| */ static FILE *save_file; /* the file being written */ @ @<External f...@>= long save_graph(g,f) Graph *g; /* graph to be saved */ char *f; /* name of the file to be created */ {@+@<Local variables for |save_graph|@>@;@# if (g==NULL || g->vertices==NULL) return -1; /* where is |g|? */ anomalies=0; @<Figure out the extent of |g|'s internal records@>; save_file=fopen(f,"w"); if (!save_file) return -2; /* oops, the operating system won't cooperate */ @<Translate |g| into external format@>; @<Make notes at the end of the file about any changes that were necessary@>; fclose(save_file); gb_free(working_storage); return anomalies; } @ The main difficulty faced by |save_graph| is the problem of translating vertex and arc pointers into symbolic form. A graph's vertices usually appear in a single block, |g->vertices|, but its arcs usually appear in separate blocks that were created whenever the |gb_new_arc| routine needed more space. Other blocks, created by |gb_save_string|, are usually also present in the |g->data| area. We need to classify the various data blocks. We also want to be able to handle graphs that have been created with homegrown methods of memory allocation, because GraphBase structures need not conform to the conventions of |gb_new_arc| and |gb_save_string|. A simple data structure based on \&{block\_rep} records will facilitate our task. Each \&{block\_rep} will be set up to contain the information we need to know about a particular block of data accessible from |g->data|. Such blocks are classified into four categories, identified by the |cat| field in a \&{block\_rep}: @d unk 0 /* |cat| value for blocks of unknown nature */ @d ark 1 /* |cat| value for blocks assumed to hold |Arc| records */ @d vrt 2 /* |cat| value for blocks assumed to hold |Vertex| records */ @d mxt 3 /* |cat| value for blocks being used for more than one purpose */ @<Type...@>= typedef struct { char *start_addr; /* starting address of a data block */ char *end_addr; /* ending address of a data block */ long offset; /* index number of first record in the block, if known */ long cat; /* |cat| code for the block */ long expl; /* have we finished exploring this block? */ } block_rep; @ The |block_rep| records don't need to be linked together in any fancy way, because there usually aren't very many of them. We will simply create an array, organized in decreasing order of |start_addr| and |end_addr|, with a dummy record standing as a sentinel at the end. A system-dependent change might be necessary in the following code, if pointer values can be longer than 32 bits, or if comparisons between pointers are undefined. @^system dependencies@> @<Private v...@>= static block_rep* blocks; /* beginning of table of block representatives */ static Area working_storage; @ Initially we set the |end_addr| field to the location following a block's data area. Later we will change it as explained below. The code in this section uses the fact that all bits of storage blocks are zero until set nonzero. In particular, the |cat| field of each |block_rep| will initially be |unk|, and the |expl| will be zero; the |start_addr| and |end_addr| of the sentinel record will be zero. @<Initialize the |blocks| array@>= {@+Area t; /* variable that runs through |g->data| */ for (*t=*(g->data),block_count=0;*t;*t=(*t)->next) block_count++; blocks=gb_typed_alloc(block_count+1,block_rep,working_storage); if (blocks==NULL) return -3; /* out of memory */ for (*t=*(g->data),block_count=0;*t;*t=(*t)->next,block_count++) { cur_block=blocks+block_count; while (cur_block>blocks&&(cur_block-1)->start_addr<(*t)->first) { cur_block->start_addr=(cur_block-1)->start_addr; cur_block->end_addr=(cur_block-1)->end_addr; cur_block--; } cur_block->start_addr=(*t)->first; cur_block->end_addr=(char*)*t; } } @ @<Local variables for |save...@>= register block_rep *cur_block; /* the current block of interest */ long block_count; /* how many blocks have we processed? */ @ The |save_graph| routine makes two passes over the graph. The goal of the first pass is reconnaissance: We try to see where everything is, and we prune off parts that don't conform to the restrictions. When we get to the second pass, our task will then be almost trivial. We will be able to march through the known territory and spew out a copy of what we encounter. (Items that are ``pruned'' are not actually removed from |g| itself, only from the portion of~|g| that is saved.) The first pass is essentially a sequence of calls of the |lookup| macro, which looks at one field of one record and notes whether the existence of this field extends the known boundaries of the graph. The |lookup| macro is a shorthand notation for calling the |classify| subroutine. We make the same assumption about field sizes as the |fill_field| routine did above. @^system dependencies@> @d lookup(l,t) classify((util*)&(l),t) /* explore field |l| of type |t| */ @<Private f...@>= static void classify(l,t) util *l; /* location of field to be classified */ char t; /* its type code, from the set $\{\.Z,\.I,\.V,\.S,\.A\}$ */ {@+register block_rep *cur_block; register char* loc; register long tcat; /* category corresponding to |t| */ register long tsize; /* record size corresponding to |t| */ switch (t) { default: return; case 'V': if (l->I==1) return; tcat=vrt; tsize=sizeof(Vertex); break; case 'A': tcat=ark; tsize=sizeof(Arc); break; } if (l->I==0) return; @<Classify a pointer variable@>; } @ Here we know that |l| points to a |Vertex| or to an |Arc|, according as |tcat| is |vrt| or |ark|. We need to check that this doesn't violate any assumptions about all such pointers lying in pure blocks within the |g->data| area. @<Classify a pointer variable@>= loc=(char*)l->V; for (cur_block=blocks; cur_block->start_addr>loc; cur_block++) ; if (loc<cur_block->end_addr) { if ((loc-cur_block->start_addr)%tsize!=0 || loc+tsize>cur_block->end_addr) cur_block->cat=mxt; if (cur_block->cat==unk) cur_block->cat=tcat; else if (cur_block->cat!=tcat) cur_block->cat=mxt; } @ We go through the list of blocks repeatedly until we reach a stable situation in which every |vrt| or |ark| block has been explored. @<Figure out the extent of |g|'s internal records@>= {@+long activity; @<Initialize the |blocks| array@>; lookup(g->vertices,'V'); lookup(g->uu,g->util_types[8]); lookup(g->vv,g->util_types[9]); lookup(g->ww,g->util_types[10]); lookup(g->xx,g->util_types[11]); lookup(g->yy,g->util_types[12]); lookup(g->zz,g->util_types[13]); do@+{@+activity=0; for(cur_block=blocks;cur_block->end_addr;cur_block++) { if (cur_block->cat==vrt && !cur_block->expl) @<Explore a block of supposed vertex records@>@; else if (cur_block->cat==ark && !cur_block->expl) @<Explore a block of supposed arc records@>@; else continue; cur_block->expl=activity=1; } }@+while (activity); } @ While we are exploring a block, the |lookup| routine might classify a previously explored block (or even the current block) as |mxt|. Therefore some data we assumed would be accessible will actually be removed from the graph; contradictions that arose might no longer exist. But we plunge ahead anyway, because we aren't going to try especially hard to ``save'' portions of graphs that violate our ground rules. @<Explore a block of supposed vertex records@>= {@+register Vertex*v; for (v=(Vertex*)cur_block->start_addr;@| (char*)(v+1)<=cur_block->end_addr && cur_block->cat==vrt;v++) { lookup(v->arcs,'A'); lookup(v->u,g->util_types[0]); lookup(v->v,g->util_types[1]); lookup(v->w,g->util_types[2]); lookup(v->x,g->util_types[3]); lookup(v->y,g->util_types[4]); lookup(v->z,g->util_types[5]); } } @ @<Explore a block of supposed arc records@>= {@+register Arc*a; for (a=(Arc*)cur_block->start_addr;@| (char*)(a+1)<=cur_block->end_addr && cur_block->cat==ark;a++) { lookup(a->tip,'V'); lookup(a->next,'A'); lookup(a->a,g->util_types[6]); lookup(a->b,g->util_types[7]); } } @ OK, the first pass is complete. And the second pass is routine: @<Translate |g| into external format@>= @<Orient the |blocks| table for translation@>; @<Initialize the output buffer mechanism and output the first line@>; @<Translate the |Graph| record@>; @<Translate the |Vertex| records@>; @<Translate the |Arc| records@>; @<Output the checksum line@>; @ During this pass we decrease the |end_addr| field of a |block_rep|, so that it points to the first byte of the final record in a |vrt| or |ark| block. The variables |m| and |n| are set to the number of arc records and vertex records, respectively. @<Local variables for |save...@>= long m; /* total number of |Arc| records to be translated */ long n; /* total number of |Vertex| records to be translated */ register long s; /* accumulator register for arithmetic calculations */ @ One tricky point needs to be observed, in the unusual case that there are two or more blocks of \&{Vertex} records: The base block |g->vertices| must come first in the final ordering. (This is the only exception to the rule that \&{Vertex} and \&{Arc} records each retain their relative order with respect to less-than and greater-than.) @<Orient the |blocks| table for translation@>= m=0;@+@<Set |n| to the size of the block that starts with |g->vertices|@>; for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--) { if (cur_block->cat==vrt) { s=(cur_block->end_addr-cur_block->start_addr)/sizeof(Vertex); cur_block->end_addr=cur_block->start_addr+((s-1)*sizeof(Vertex)); if (cur_block->start_addr!=(char*)g->vertices) { cur_block->offset=n;@+ n+=s; } /* otherwise |cur_block->offset| remains zero */ }@+else if (cur_block->cat==ark) { s=(cur_block->end_addr-cur_block->start_addr)/sizeof(Arc); cur_block->end_addr=cur_block->start_addr+((s-1)*sizeof(Arc)); cur_block->offset=m; m+=s; } } @ @<Set |n| to the size of the block that starts with |g->vertices|@>= n=0; for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--) if (cur_block->start_addr==(char *)g->vertices) { n=(cur_block->end_addr-cur_block->start_addr)/sizeof(Vertex); break; } @ We will store material to be output in the |buffer| array, so that we can compute the correct checksum. @<Private v...@>= static char *buf_ptr; /* the first unfilled position in |buffer| */ static long magic; /* the checksum */ @ @<Private f...@>= static void flushout() /* output the buffer to |save_file| */ { *buf_ptr++='\n'; *buf_ptr='\0'; magic=new_checksum(buffer,magic); fputs(buffer,save_file); buf_ptr=buffer; } @ If a supposed string pointer is zero, we output the null string. (This case arises when a string field has not been initialized, for example in vertices and arcs that have been allocated but not used.) @<Private f...@>= static void prepare_string(s) char *s; /* string that is moved to |item_buf| */ {@+register char *p,*q; item_buf[0]='"'; p=&item_buf[1]; if (s==0) goto sready; for (q=s;*q&&p<=&item_buf[MAX_SV_STRING];q++,p++) if (*q=='"'||*q=='\n'||*q=='\\'||imap_ord(*q)==unexpected_char) { anomalies |= bad_string_char; *p='?'; }@+else *p=*q; if (*q) anomalies |= string_too_long; sready: *p='"'; *(p+1)='\0'; } @ The main idea of this part of the program is to format an item into |item_buf|, then move it to |buffer|, making sure that there is always room for a comma. @d append_comma *buf_ptr++=',' @<Private f...@>= static void move_item() {@+register long l=strlen(item_buf); if (buf_ptr+l>&buffer[78]) { if (l<=78) flushout(); else {@+register char *p=item_buf; if (buf_ptr>&buffer[77]) flushout(); /* no room for initial \.{\char`\"} */ do@+{ for (;buf_ptr<&buffer[78];buf_ptr++,p++,l--) *buf_ptr=*p; *buf_ptr++='\\'; flushout(); }@+while(l>78); strcpy(buffer,p); buf_ptr=&buffer[l]; return; } } strcpy(buf_ptr,item_buf); buf_ptr+=l; } @ @<Initialize the output buffer mechanism and output the first line@>= buf_ptr=buffer; magic=0; fputs("* GraphBase graph (util_types ",save_file); {@+register char*p; for (p=g->util_types;p<g->util_types+14;p++) if (*p=='Z'||*p=='I'||*p=='V'||*p=='S'||*p=='A') fputc(*p,save_file); else fputc('Z',save_file); } fprintf(save_file,",%ldV,%ldA)\n",n,m); @ A macro called |trans|, which is sort of an inverse to |fillin|, takes care of the main work in the second pass. @d trans(l,t) translate_field((util*)&(l),t) @<Private f...@>= static void translate_field(l,t) util *l; /* address of field to be output in symbolic form */ char t; /* type of formatting desired */ {@+register block_rep *cur_block; register char* loc; register long tcat; /* category corresponding to |t| */ register long tsize; /* record size corresponding to |t| */ if (comma_expected) append_comma; else comma_expected=1; switch (t) { default: anomalies|=bad_type_code; /* fall through to case \.Z */ case 'Z': buf_ptr--; /* forget spurious comma */ if (l->I) anomalies|=ignored_data; return; case 'I': numeric: sprintf(item_buf,"%ld",l->I);@+goto ready; case 'S': prepare_string(l->S);@+goto ready; case 'V': if (l->I==1) goto numeric; tcat=vrt;@+tsize=sizeof(Vertex);@+break; case 'A': tcat=ark;@+tsize=sizeof(Arc);@+break; } @<Translate a pointer variable@>; ready:move_item(); } @ @<Translate a pointer variable@>= loc=(char*)l->V; item_buf[0]='0';@+item_buf[1]='\0'; /* |NULL| will be the default */ if (loc==NULL) goto ready; for (cur_block=blocks; cur_block->start_addr>loc; cur_block++) ; if (loc>cur_block->end_addr) { anomalies|=addr_not_in_data_area; goto ready; } if (cur_block->cat!=tcat||(loc-cur_block->start_addr)%tsize!=0) { anomalies|=addr_in_mixed_block; goto ready; } sprintf(item_buf,"%c%ld",t, cur_block->offset+((loc-cur_block->start_addr)/tsize)); @ @<Translate the |Graph| record@>= prepare_string(g->id); if (strlen(g->id)>MAX_SV_ID) { strcpy(item_buf+MAX_SV_ID+1,"\""); anomalies|=string_too_long; } move_item(); comma_expected=1; trans(g->n,'I'); trans(g->m,'I'); trans(g->uu,g->util_types[8]); trans(g->vv,g->util_types[9]); trans(g->ww,g->util_types[10]); trans(g->xx,g->util_types[11]); trans(g->yy,g->util_types[12]); trans(g->zz,g->util_types[13]); flushout(); @ @<Translate the |Vertex| records@>= {@+register Vertex* v; fputs("* Vertices\n",save_file); for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--) if (cur_block->cat==vrt && cur_block->offset==0) @<Translate all |Vertex| records in |cur_block|@>; for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--) if (cur_block->cat==vrt && cur_block->offset!=0) @<Translate all |Vertex| records in |cur_block|@>; } @ @<Translate all |Vertex| records in |cur_block|@>= for (v=(Vertex*)cur_block->start_addr; v<=(Vertex*)cur_block->end_addr;v++) { comma_expected=0; trans(v->name,'S'); trans(v->arcs,'A'); trans(v->u,g->util_types[0]); trans(v->v,g->util_types[1]); trans(v->w,g->util_types[2]); trans(v->x,g->util_types[3]); trans(v->y,g->util_types[4]); trans(v->z,g->util_types[5]); flushout(); } @ @<Translate the |Arc| records@>= {@+register Arc* a; fputs("* Arcs\n",save_file); for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--) if (cur_block->cat==ark) for (a=(Arc*)cur_block->start_addr;a<=(Arc*)cur_block->end_addr;a++) { comma_expected=0; trans(a->tip,'V'); trans(a->next,'A'); trans(a->len,'I'); trans(a->a,g->util_types[6]); trans(a->b,g->util_types[7]); flushout(); } } @ @<Output the checksum line@>= fprintf(save_file,"* Checksum %ld\n",magic); @ @<Make notes at the end of the file about any changes that were necessary@>= if (anomalies) { fputs("> WARNING: I had trouble making this file from the given graph!\n", save_file); if (anomalies&bad_type_code) fputs(">> The original util_types had to be corrected.\n",save_file); if (anomalies&ignored_data) fputs(">> Some data suppressed by Z format was actually nonzero.\n", save_file); if (anomalies&string_too_long) fputs(">> At least one long string had to be truncated.\n", save_file); if (anomalies&bad_string_char) fputs(">> At least one string character had to be changed to '?'.\n", save_file); if (anomalies&addr_not_in_data_area) fputs(">> At least one pointer led out of the data area.\n",save_file); if (anomalies&addr_in_mixed_block) fputs(">> At least one data block had an illegal mixture of records.\n", save_file); if (anomalies&(addr_not_in_data_area+addr_in_mixed_block)) fputs(">> (Pointers to improper data have been changed to 0.)\n", save_file); fputs("> You should be able to read this file with restore_graph,\n", save_file); fputs("> but the graph you get won't be exactly like the original.\n", save_file); } @* Index. Here is a list that shows where the identifiers of this program are defined and used. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gb_types.w������������������������������������������������������������������������������������������0000444�0001750�0001750�00000000233�05411251721�011176� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@q This file makes CWEAVE treat Graph, Arc, Vertex, etc. as reserved words. @> @s Graph int @s Arc int @s Vertex int @s Area int @s util int @s siz_t int ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test_sample.w���������������������������������������������������������������������������������������0000444�0001750�0001750�00000024734�05440406041�011715� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% 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{TEST\_\,SAMPLE} @* Introduction. This GraphBase program is intended to be used only when the Stanford GraphBase is being installed. It invokes the most critical subroutines and creates a file that can be checked against the correct output. The testing is not exhaustive by any means, but it is designed to detect errors of portability---cases where different results might occur on different systems. Thus, if nothing goes wrong, one can assume that the GraphBase routines are probably installed satisfactorily. The basic idea of {\sc TEST\_\,SAMPLE} is quite simple: We generate a graph, then print out a few of its salient characteristics. Then we recycle the graph and generate another, etc. The test is passed if the output file matches a ``correct'' output file generated at Stanford by the author. Actually there are two output files. The main one, containing samples of graph characteristics, is the standard output. The other, called \.{test.gb}, is a graph that has been saved in ASCII format with |save_graph|. @p #include "gb_graph.h" /* we use the {\sc GB\_\,GRAPH} data structures */ #include "gb_io.h" /* and the GraphBase input/output routines */ @<Include headers for all of the GraphBase generation modules@>@; @# @<Private variables@>@; @<Procedures@>@; @t\4@>int main() {@+Graph *g,*gg;@+long i;@+Vertex *v; /* temporary registers */ printf("GraphBase samples generated by test_sample:\n"); @<Save a graph to be restored later@>; @<Print samples of generated graphs@>; return 0; /* normal exit */ } @ @<Include headers for all of the GraphBase generation modules@>= #include "gb_basic.h" /* we test the basic graph operations */ #include "gb_books.h" /* and the graphs based on literature */ #include "gb_econ.h" /* and the graphs based on economic data */ #include "gb_games.h" /* and the graphs based on football scores */ #include "gb_gates.h" /* and the graphs based on logic circuits */ #include "gb_lisa.h" /* and the graphs based on Mona Lisa */ #include "gb_miles.h" /* and the graphs based on mileage data */ #include "gb_plane.h" /* and the planar graphs */ #include "gb_raman.h" /* and the Ramanujan graphs */ #include "gb_rand.h" /* and the random graphs */ #include "gb_roget.h" /* and the graphs based on Roget's Thesaurus */ #include "gb_save.h" /* and we save results in ASCII format */ #include "gb_words.h" /* and we also test five-letter-word graphs */ @ The subroutine |print_sample(g,n)| will be specified later. It prints global characteristics of |g| and local characteristics of the |n|th vertex. We begin the test cautiously by generating a graph that requires no input data and no pseudo-random numbers. If this test fails, the fault must lie either in {\sc GB\_\,GRAPH} or {\sc GB\_\,RAMAN}. @<Print samples of generated graphs@>= print_sample(raman(31L,3L,0L,4L),4); @ Next we test part of {\sc GB\_\,BASIC} that relies on a particular interpretation of the operation `|w>>=1|'. If this part of the test fails, please look up `system dependencies' in the index to {\sc GB\_\,BASIC}, and correct the problem on your system by making a change file \.{gb\_basic.ch}. (See \.{queen\_wrap.ch} for an example of a change file.) On the other hand, if {\sc TEST\_\,SAMPLE} fails only in this particular test while passing all those that follow, chances are excellent that you have a pretty good implementation of the GraphBase anyway, because the bug detected here will rarely show up in practice. Ask yourself: Can I live comfortably with such a bug? @<Print samples of generated graphs@>= print_sample(board(1L,1L,2L,-33L,1L,-0x40000000L-0x40000000L,1L),2000); /* coordinates 32 and 33 (only) should wrap around */ @ Another system-dependent part of {\sc GB\_\,BASIC} is tested here, this time involving character codes. @<Print samples of generated graphs@>= print_sample(subsets(32L,18L,16L,0L,999L,-999L,0x80000000L,1L),1); @ If \.{test.gb} fails to match \.{test.correct}, the most likely culprit is |vert_offset|, a ``pointer hack'' in {\sc GB\_\,BASIC}. That macro absolutely must be made to work properly, because it is used heavily. In particular, it is used in the |complement| routine tested here, and in the |gunion| routine tested below. @<Save a graph to be restored later@>= g=random_graph(3L,10L,1L,1L,0L,NULL,dst,1L,2L,1L); /* a random multigraph with 3 vertices, 10 edges */ gg=complement(g,1L,1L,0L); /* a copy of |g|, without multiple edges */ v=gb_typed_alloc(1,Vertex,gg->data); /* we create a stray vertex too */ v->name=gb_save_string("Testing"); gg->util_types[10]='V'; gg->ww.V=v; /* the stray vertex is now part of |gg| */ save_graph(gg,"test.gb"); /* so it will appear in \.{test.gb} (we hope) */ gb_recycle(g);@+gb_recycle(gg); @ @<Private...@>= static long dst[]={0x20000000,0x10000000,0x10000000}; /* a probability distribution with frequencies 50\%, 25\%, 25\% */ @ Now we try to reconstruct the graph we saved before, and we also randomize its lengths. @<Print samples...@>= g=restore_graph("test.gb"); if (i=random_lengths(g,0L,10L,12L,dst,2L)) printf("\nFailure code %ld returned by random_lengths!\n",i); else { gg=random_graph(3L,10L,1L,1L,0L,NULL,dst,1L,2L,1L); /* same as before */ print_sample(gunion(g,gg,1L,0L),2); gb_recycle(g);@+gb_recycle(gg); } @ Partial evaluation of a RISC circuit involves fairly intricate pointer manipulation, so this step should help to test the portability of the author's favorite programming tricks. @<Print samples...@>= print_sample(partial_gates(risc(0L),1L,43210L,98765L,NULL),79); @ Now we're ready to test the mechanics of reading data files, sorting with {\sc GB\_\,SORT}, and heavy randomization. Lots of computation takes place in this section. @<Print samp...@>= print_sample(book("homer",500L,400L,2L,12L,10000L,-123456L,789L),81); print_sample(econ(40L,0L,400L,-111L),11); print_sample(games(60L,70L,80L,-90L,-101L,60L,0L,999999999L),14); print_sample(miles(50L,-500L,100L,1L,500L,5L,314159L),20); print_sample(plane_lisa(100L,100L,50L,1L,300L,1L,200L, 50L*299L*199L,200L*299L*199L),1294); print_sample(plane_miles(50L,500L,-100L,1L,1L,40000L,271818L),14); print_sample(random_bigraph(300L,3L,1000L,-1L,0L,dst,-500L,500L,666L),3); print_sample(roget(1000L,3L,1009L,1009L),40); @ Finally, here's a picky, picky test that is supposed to fail the first time, succeed the second. (The weight vector just barely exceeds the maximum weight threshold allowed by {\sc GB\_WORDS}. That test is ultraconservative, but eminently reasonable nevertheless.) @<Print samples...@>= print_sample(words(100L,wt_vector,70000000L,69L),5); wt_vector[1]++; print_sample(words(100L,wt_vector,70000000L,69L),5); print_sample(words(0L,NULL,0L,69L),5555); @ @<Private...@>= static long wt_vector[]= {100,-80589,50000,18935,-18935,18935,18935,18935,18935}; @* Printing the sample data. Given a graph |g| in GraphBase format and an integer~|n|, the subroutine |print_sample(g,n)| will output global characteristics of~|g|, such as its name and size, together with detailed information about its |n|th vertex. Then |g| will be shredded and recycled; the calling routine should not refer to it again. @<Procedures@>= static void pr_vert(); /* a subroutine for printing a vertex is declared below */ static void pr_arc(); /* likewise for arcs */ static void pr_util(); /* and for utility fields in general */ static void print_sample(g,n) Graph *g; /* graph to be sampled and destroyed */ int n; /* index to the sampled vertex */ { printf("\n"); if (g==NULL) { printf("Ooops, we just ran into panic code %ld!\n",panic_code); if (io_errors) printf("(The I/O error code is 0x%lx)\n",(unsigned long)io_errors); }@+else { @<Print global characteristics of |g|@>; @<Print information about the |n|th vertex@>; gb_recycle(g); } } @ The graph's |util_types| are used to determine how much information should be printed. A level parameter also helps control the verbosity of printout. In the most verbose mode, each utility field that points to a vertex or arc, or contains integer or string data, will be printed. @<Procedures@>= static void pr_vert(v,l,s) Vertex *v; /* vertex to be printed */ int l; /* |<=0| if the output should be terse */ char *s; /* format for graph utility fields */ { if (v==NULL) printf("NULL"); else if (is_boolean(v)) printf("ONE"); /* see {\sc GB\_\,GATES} */ else { printf("\"%s\"",v->name); pr_util(v->u,s[0],l-1,s); pr_util(v->v,s[1],l-1,s); pr_util(v->w,s[2],l-1,s); pr_util(v->x,s[3],l-1,s); pr_util(v->y,s[4],l-1,s); pr_util(v->z,s[5],l-1,s); if (l>0) {@+register Arc *a; for (a=v->arcs;a;a=a->next) { printf("\n "); pr_arc(a,1,s); } } } } @ @<Pro...@>= static void pr_arc(a,l,s) Arc *a; /* non-null arc to be printed */ int l; /* |<=0| if the output should be terse */ char *s; /* format for graph utility fields */ { printf("->"); pr_vert(a->tip,0,s); if (l>0) { printf( ", %ld",a->len); pr_util(a->a,s[6],l-1,s); pr_util(a->b,s[7],l-1,s); } } @ @<Procedures@>= static void pr_util(u,c,l,s) util u; /* a utility field to be printed */ char c; /* its type code */ int l; /* 0 if output should be terse, |-1| if pointers omitted */ char *s; /* utility types for overall graph */ { switch (c) { case 'I': printf("[%ld]",u.I);@+break; case 'S': printf("[\"%s\"]",u.S?u.S:"(null)");@+break; case 'A': if (l<0) break; printf("["); if (u.A==NULL) printf("NULL"); else pr_arc(u.A,l,s); printf("]"); break; case 'V': if (l<0) break; /* avoid infinite recursion */ printf("["); pr_vert(u.V,l,s); printf("]"); default: break; /* case |'Z'| does nothing, other cases won't occur */ } } @ @<Print information about the |n|th vertex@>= printf("V%d: ",n); if (n>=g->n || n<0) printf("index is out of range!\n"); else { pr_vert(g->vertices+n,1,g->util_types); printf("\n"); } @ @<Print global characteristics of |g|@>= printf("\"%s\"\n%ld vertices, %ld arcs, util_types %s", g->id,g->n,g->m,g->util_types); pr_util(g->uu,g->util_types[8],0,g->util_types); pr_util(g->vv,g->util_types[9],0,g->util_types); pr_util(g->ww,g->util_types[10],0,g->util_types); pr_util(g->xx,g->util_types[11],0,g->util_types); pr_util(g->yy,g->util_types[12],0,g->util_types); pr_util(g->zz,g->util_types[13],0,g->util_types); printf("\n"); @* Index. We end with the customary list of identifiers, showing where they are used and where they are defined. ������������������������������������queen_wrap.ch���������������������������������������������������������������������������������������0000444�0001750�0001750�00000011017�05440401246�011657� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% This file is part of the Stanford GraphBase (c) Stanford University 1993 It's a demonstration "change file", which converts the demonstration program called "queen" into a similar demonstration program called "queen_wrap". Change files make it easy to modify CWEB source programs without touching the master files, thereby remaining totally compatible with all other users. Anybody can make whatever modifications they like in change files, but everybody is supposed to leave the master files intact. Please also leave the present file intact, so that it remains as a useful demonstration of the change-file idea. The format of change files is simple: First comes a line that begins with @x, then comes a line that is a verbatim copy of some line from the master file, followed by zero or more additional lines that should match the subsequent lines of the master file. Then you say @y, and then you give replacement lines for everything between @x and @y in the master file. Then you say @z. All changes must occur in the order of replaced text in the master file, and must be uniquely identifiable by the first line that follows @x. Optional comments may follow @x, @y, or @z on a line, and may occur outside of @x-@y-@z groups. In fact, you are now reading such an optional comment. @x replace the copyright notice by a change notice @i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES! @y \let\maybe=\iffalse % tell CWEB to print only sections that change \def\prerequisite#1{} \def\prerequisites#1#2{} % disable boilerplate macros \def\botofcontents{\vskip 0pt plus 1filll \parskip=0pt This program was obtained by modifying {\sc QUEEN} in the Stanford GraphBase.\par Only sections that have changed are listed here.\par} @z @x change the program title \def\title{QUEEN} @y \def\title{QUEEN\_WRAP} @z @x now we modify the introductory remarks of section 1 An ASCII file called \.{queen.gb} is also produced. Other programs can obtain a copy of the queen graph by calling |restore_graph("queen.gb")|. You might find it interesting to compare the output of {\sc QUEEN} with the contents of \.{queen.gb}; the former is intended to be readable by human beings, the latter by computers. @y Unlike an ordinary chessboard, the board considered here ``wraps around'' at the left and right edges, so that it is essentially a cylinder. It does not, however, wrap around at the top and bottom; double wrapping would actually allow a lowly bishop to move from any given cell to any other, in two different ways. An ASCII file called \.{queen\_wrap.gb} is also produced. Other programs can obtain a copy of the graph by calling |restore_graph("queen_wrap.gb")|. You might find it interesting to compare the output of {\sc QUEEN\_WRAP} with the contents of \.{queen\_wrap.gb}; the former is intended to be readable by human beings, the latter by computers. @z @x changes to the code of section 1 g=board(3L,4L,0L,0L,-1L,0L,0L); /* a graph with rook moves */ gg=board(3L,4L,0L,0L,-2L,0L,0L); /* a graph with bishop moves */ ggg=gunion(g,gg,0L,0L); /* a graph with queen moves */ save_graph(ggg,"queen.gb"); /* generate an ASCII file for |ggg| */ @y we add wraparound g=board(3L,4L,0L,0L,-1L,2L,0L); /* a graph with rook moves and wrapping */ /* we set |wrap=2| because only the second coordinate wraps */ gg=board(3L,4L,0L,0L,-2L,2L,0L); /* a graph with bishop moves and wrapping */ ggg=gunion(g,gg,0L,0L); /* a graph with queen moves and wrapping */ save_graph(ggg,"queen_wrap.gb"); /* generate an ASCII file for |ggg| */ @z @x change to the code of section 2 printf("Queen Moves on a 3x4 Board\n\n"); @y printf("Queen Moves on a Cylindrical 3x4 Board\n\n"); @z A change file is usually much shorter than the master file, but the present one is an exception because the master file itself is short. You can use many different change files with the same master file. To run the queen_wrap program on a UNIX system, you can say ctangle queen.w queen_wrap.ch queen_wrap.c and then compile and go. (The .w is optional in the first argument to ctangle; the .ch is optional in the second; the .c is optional in the third.) The C compiler and debugger will refer to appropriate lines of the original source file queen.w and/or the change file queen_wrap.ch when you are troubleshooting. You need never look at the file queen_wrap.c that was output by ctangle, although the compiler and debugger will want to see it. To obtain a TeXed documentation, you can say cweave queen queen_wrap tex queen rm queen.tex after which you print the file queen.dvi output by TeX. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������word_giant.ch���������������������������������������������������������������������������������������0000444�0001750�0001750�00000010577�05407371040�011661� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% This file is part of the Stanford GraphBase (c) Stanford University 1993 It's a demonstration "change file", which modifies the demonstration program word_components. To use it on a UNIX system, say ctangle word_components word_giant word_giant make word_giant word_giant and you should find a file word_giant.gb that contains a useful graph. (Try testing this graph with "miles_span -gword_giant.gb".) See queen_wrap.ch for comments on the general form of change files. @x replace the copyright notice by a change notice @i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES! @y \let\maybe=\iffalse % print only sections that change \def\prerequisite#1{} \def\prerequisites#1#2{} % disable boilerplate macros \def\botofcontents{\vskip 0pt plus 1filll \parskip=0pt This program was obtained by modifying {\sc WORD\_\,COMPONENTS} in the Stanford GraphBase.\par Only sections that have changed are listed here.\par} @z @x change the program title \def\title{WORD\_\,COMPONENTS} @y \def\title{WORD\_\,GIANT} @z @x here we modify the introductory remarks of section 1 @* Components. \kern-.7pt This simple demonstration program computes the connected components of the GraphBase graph of five-letter words. It prints the words in order of decreasing weight, showing the number of edges, components, and isolated vertices present in the graph defined by the first $n$ words for all~$n$. @y @* Components. This simple demonstration program computes the largest connected component of the GraphBase graph of five-letter words, and saves it in file \.{word\_giant.gb}. It modifies the edge lengths so that alphabetic distances are used (as in the \.{-a} option of {\sc LADDERS}). @z @x include additional header files in section 1 #include "gb_graph.h" /* the GraphBase data structures */ @y #include "gb_graph.h" /* the GraphBase data structures */ #include "gb_save.h" /* the |save_graph| routine */ #include "gb_basic.h" /* the |induced| routine */ @z @x changes to the code of section 1 printf("Component analysis of %s\n",g->id); for (v=g->vertices; v<g->vertices+g->n; v++) { n++, printf("%4ld: %5ld %s",n,v->weight,v->name); @<Add vertex |v| to the component structure, printing out any components it joins@>; printf("; c=%ld,i=%ld,m=%ld\n", comp, isol, m); } @<Display all unusual components@>; @y we suppress printing for (v=g->vertices; v<g->vertices+g->n; v++) { n++; @<Add vertex |v| to the component structure, and change the lengths of edges that connect it to previous vertices@>; } @<Mark all vertices of the giant component@>; save_graph(induced(g,"giant",0,0,0),"word_giant.gb"); @z @x change to the code of section 2 if (!a) printf("[1]"); /* indicate that this word is isolated */ else {@+long c=0; /* the number of merge steps performed because of |v| */ for (; a; a=a->next) {@+register Vertex *u=a->tip; m++; @<Merge the components of |u| and |v|, if they differ@>; } printf(" in %s[%ld]", v->master->name, v->master->size); /* show final component */ } @y for (; a; a=a->next) {@+register Vertex *u=a->tip; register int k=a->loc; /* where the words differ */ register char *p=v->name+k,*q=u->name+k; if (*p<*q) a->len=(a-1)->len=*q-*p; else a->len=(a-1)->len=*p-*q; /* alphabetic distance */ m++; @<Merge the components of |u| and |v|, if they differ@>; } @z @x delete printing in section 4 if (c++>0) printf("%s %s[%ld]", (c==2? " with": ","), u->name, u->size); @y @z @x delete more printing in section 4 if (c++>0) printf("%s %s[%ld]", (c==2? " with": ","), w->name, w->size); @y @z @x replace section 5 We consider all other components unusual, so we print them out when the other computation is done. @<Display all unusual components@>= printf( "\nThe following non-isolated words didn't join the giant component:\n"); for (v=g->vertices; v<g->vertices+g->n; v++) if (v->master==v && v->size>1 && v->size+v->size<g->n) {@+register Vertex *u; long c=1; /* count of number printed on current line */ printf("%s", v->name); for (u=v->link; u!=v; u=u->link) { if (c++==12) putchar('\n'),c=1; printf(" %s",u->name); } putchar('\n'); } @y We set the |ind| field to 1 in the giant component, so that the |induced| routine will retain those vertices. @<Mark...@>= for (v=g->vertices; v<g->vertices+g->n; v++) if (v->master->size+v->master->size<g->n) v->ind=0; else v->ind=1; @z ���������������������������������������������������������������������������������������������������������������������������������Makefile��������������������������������������������������������������������������������������������0000644�0001750�0001750�00000011041�07031763714�010645� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # Makefile for the Stanford GraphBase # # Be sure that CWEB version 3.0 or greater is installed before proceeding! # Skip down to "SHORTCUT" if you're going to work only from the # current directory. (Not recommended for serious users.) # Change SGBDIR to the directory where all GraphBase files will go: SGBDIR = /usr/local/sgb # Change DATADIR to the directory where GraphBase data files will go: DATADIR = $(SGBDIR)/data # Change INCLUDEDIR to the directory where GraphBase header files will go: INCLUDEDIR = $(SGBDIR)/include # Change LIBDIR to the directory where GraphBase library routines will go: LIBDIR = /usr/local/lib # Change BINDIR to the directory where installdemos will put demo programs: BINDIR = /usr/local/bin # Change CWEBINPUTS to the directory where CWEB include files will go: CWEBINPUTS = /usr/local/lib/cweb # SHORTCUT: Uncomment these lines, for single-directory installation: #DATADIR = . #INCLUDEDIR = . #LIBDIR = . #BINDIR = . #CWEBINPUTS = . # Uncomment the next line if your C uses <string.h> but not <strings.h>: #SYS = -DSYSV # If you prefer optimization to debugging, change -g to something like -O: CFLAGS = -g -I$(INCLUDEDIR) $(SYS) ########## You shouldn't have to change anything after this point ########## LDFLAGS = -L. -L$(LIBDIR) LDLIBS = -lgb LOADLIBES = $(LDLIBS) .SUFFIXES: .dvi .tex .w .tex.dvi: tex $*.tex .w.c: if test -r $*.ch; then ctangle $*.w $*.ch; else ctangle $*.w; fi .w.tex: if test -r $*.ch; then cweave $*.w $*.ch; else cweave $*.w; fi .w.o: make $*.c make $*.o .w: make $*.c make $* .w.dvi: make $*.tex make $*.dvi DATAFILES = anna.dat david.dat econ.dat games.dat homer.dat huck.dat \ jean.dat lisa.dat miles.dat roget.dat words.dat KERNELFILES = gb_flip.w gb_graph.w gb_io.w gb_sort.w GENERATORFILES = gb_basic.w gb_books.w gb_econ.w gb_games.w gb_gates.w \ gb_lisa.w gb_miles.w gb_plane.w gb_raman.w gb_rand.w gb_roget.w \ gb_words.w DEMOFILES = assign_lisa.w book_components.w econ_order.w football.w \ girth.w ladders.w miles_span.w multiply.w queen.w roget_components.w \ take_risc.w word_components.w MISCWEBS = boilerplate.w gb_dijk.w gb_save.w gb_types.w test_sample.w CHANGEFILES = queen_wrap.ch word_giant.ch MISCFILES = Makefile README abstract.plaintex cities.texmap blank.w \ sample.correct test.correct test.dat +The+Stanford+GraphBase+ ALL = $(DATAFILES) $(KERNELFILES) $(GENERATORFILES) $(DEMOFILES) \ $(MISCWEBS) $(CHANGEFILES) $(MISCFILES) OBJS = $(KERNELFILES:.w=.o) $(GENERATORFILES:.w=.o) gb_dijk.o gb_save.o HEADERS = $(OBJS:.o=.h) DEMOS = $(DEMOFILES:.w=) help: @ echo "First 'make tests';" @ echo "then (optionally) become superuser;" @ echo "then 'make install';" @ echo "then (optionally) 'make installdemos';" @ echo "then (optionally) 'make clean'." lib: libgb.a libgb.a: $(OBJS) rm -f certified ar rcv libgb.a $(OBJS) - ranlib libgb.a gb_io.o: gb_io.c $(CC) $(CFLAGS) -DDATA_DIRECTORY=\"$(DATADIR)/\" -c gb_io.c test_io: gb_io.o $(CC) $(CFLAGS) test_io.c gb_io.o -o test_io test_graph: gb_graph.o $(CC) $(CFLAGS) test_graph.c gb_graph.o -o test_graph test_flip: gb_flip.o $(CC) $(CFLAGS) test_flip.c gb_flip.o -o test_flip tests: test_io test_graph test_flip ./test_io ./test_graph ./test_flip make gb_sort.o make lib make test_sample - ./test_sample > sample.out diff test.gb test.correct diff sample.out sample.correct rm test.gb sample.out test_io test_graph test_flip test_sample echo "Congratulations --- the tests have all been passed." touch certified install: lib if test ! -r certified; then echo "Please run 'make tests' first!"; fi test -r certified make installdata - mkdir $(LIBDIR) - cp libgb.a $(LIBDIR) - mkdir $(CWEBINPUTS) - cp -p boilerplate.w gb_types.w $(CWEBINPUTS) - mkdir $(INCLUDEDIR) - cp -p $(HEADERS) Makefile $(INCLUDEDIR) installdata: $(DATAFILES) - mkdir $(SGBDIR) - mkdir $(DATADIR) - cp -p $(DATAFILES) $(DATADIR) installdemos: lib $(DEMOS) - mkdir $(BINDIR) - mv $(DEMOS) $(BINDIR) uninstalldemos: - cd $(BINDIR); rm -f $(DEMOS) doc: tex abstract.plaintex clean: rm -f *~ *.o *.c *.h libgb.a certified \ *.tex *.log *.dvi *.toc *.idx *.scn core veryclean: clean rm -f $(DEMOS) sgb.tar: $(ALL) tar cvf sgb.tar $(ALL) floppy: $(ALL) bar cvf /dev/rfd0 $(ALL) bar tvf /dev/rfd0 eject fullfloppy: $(ALL) ERRATA ANSI AMIGA PROTOTYPES MSVC bar cvf /dev/rfd0 $(ALL) ERRATA ANSI AMIGA PROTOTYPES MSVC bar tvf /dev/rfd0 eject fulltar: $(ALL) ERRATA ANSI AMIGA PROTOTYPES MSVC tar cvf sgb.tar $(ALL) ERRATA ANSI AMIGA PROTOTYPES MSVC �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������README����������������������������������������������������������������������������������������������0000444�0001750�0001750�00000031651�10601511177�010063� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������LATE-BREAKING NEWS APPEARS AT THE END OF THIS FILE! The Stanford GraphBase is copyright 1993 by Stanford University These files may be freely copied and distributed, provided that no changes whatsoever are made. All users are asked to help keep the Stanford GraphBase sources consistent and ``uncorrupted,'' identical everywhere in the world. Changes are permissible only if the changed file is given a new name, different from the names of existing files listed below, and only if the changed file is clearly identified as not being part of the Stanford GraphBase. The author has tried his best to produce correct and useful programs, in order to help promote computer science research, but no warranty of any kind should be assumed. FILES INCLUDED IN STANDARD GRAPHBASE DISTRIBUTION The standard Stanford GraphBase consists of the following files: 1) Data files anna.dat Anna Karenina (used by gb_books) david.dat David Copperfield (used by gb_books) econ.dat US economic input and output (used by gb_econ) games.dat College football scores, 1990 (used by gb_games) homer.dat The Iliad (used by gb_books) huck.dat Huckleberry Finn (used by gb_books) jean.dat Les Miserables (used by gb_books) lisa.dat Mona Lisa pixels (used by gb_lisa) miles.dat Mileage between North American cities (used by gb_miles) roget.dat Cross references in Roget's Thesaurus (used by gb_roget) words.dat Five-letter words of English (used by (gb_words) 2) CWEB program files a) Kernel routines gb_flip.w System-independent random number generator gb_graph.w Data structures for graphs gb_io.w Input/output routines gb_sort.w Sorting routine for linked lists b) Graph generating routines gb_basic.w Standard building blocks and graph operations gb_books.w Graphs based on world literature gb_econ.w Graphs based on US inter-industry flow gb_games.w Graphs based on college football games gb_gates.w Graphs based on combinational logic gb_lisa.w Graphs based on Leonardo's Mona Lisa gb_miles.w Graphs based on highway distances gb_plane.w Planar graphs gb_raman.w Ramanujan graphs (expanders) gb_rand.w Random graphs gb_roget.w Graphs based on Roget's Thesaurus gb_words.w Graphs based on 5-letter words of English c) Demonstration routines assign_lisa.w The assignment problem, using Mona Lisa book_components.w Biconnected components, using the plots of books econ_order.w Heuristic solution to an optimum permutation problem football.w Heuristic solution to a longest-path problem girth.w Empirical study of Ramanujan graphs ladders.w Shortest paths in word graphs miles_span.w Comparison of algorithms for minimum spanning tree multiply.w Using a parallel multiplication circuit queen.w Graphs based on queen moves roget_components.w Strong components of a directed graph take_risc.w Using a simple RISC computer circuit word_components.w Connected components of word graphs d) Miscellaneous routines boilerplate.w Legalese incorporated into all GraphBase programs gb_dijk.w Variants of Dijkstra's algorithm for shortest paths gb_save.w Converting graphs to ASCII files and vice versa gb_types.w GraphBase reserved word formatting (used with @i) test_sample.w Test routine for GraphBase installation 3) Miscellaneous files Makefile Instructions to build everything with UNIX README What you're now reading abstract.plaintex Short explanation of what it's all about cities.texmap TeXable map of the 128 cities in miles.dat queen_wrap.ch Demonstration changefile sample.correct Correct primary output of test_sample test.correct Correct secondary output of test_sample test.dat Weird data used to test gb_io word_giant.ch Another demonstration changefile blank.w Template to copy when writing a new CWEB program +The+Stanford+GraphBase+ Empty file at beginning of directory listing TO INSTALL THESE PROGRAMS First install CWEB (version 3.0 or greater), which can be found in various archives; the master files reside at ftp.cs.stanford.edu. Then, on a UNIX-like system, edit the Makefile as Makefile instructs you, take a deep breath, and "make tests". After you get the message Congratulations --- the tests have all been passed you can then say "make install" (possibly changing to superuser if the directories are protected). On other systems, build the programs yourself by following the recipes in Makefile as closely as you can. On a UNIX-like system, the process of building everything should produce roughly the following actions (possibly with harmless warning messages): ctangle gb_io.w cc -g -I$INCLUDEDIR -DDATA_DIRECTORY=\"$DATADIR/\" -c gb_io.c cc -g -I$INCLUDEDIR test_io.c gb_io.o -o test_io ctangle gb_graph.w cc -g -I$INCLUDEDIR -c gb_graph.c cc -g -I$INCLUDEDIR test_graph.c gb_graph.o -o test_graph ctangle gb_flip.w cc -g -I$INCLUDEDIR -c gb_flip.c cc -g -I$INCLUDEDIR test_flip.c gb_flip.o -o test_flip test_io OK, the gb_io routines seem to work! test_graph Hey, I allocated 10000000 bytes successfully. Terrific... OK, the gb_graph routines seem to work! test_flip OK, the gb_flip routines seem to work! ctangle gb_sort.w cc -g -I$INCLUDEDIR -c gb_sort.c ctangle gb_basic.w cc -g -I$INCLUDEDIR -c gb_basic.c ctangle gb_books.w cc -g -I$INCLUDEDIR -c gb_books.c ctangle gb_econ.w cc -g -I$INCLUDEDIR -c gb_econ.c ctangle gb_games.w cc -g -I$INCLUDEDIR -c gb_games.c ctangle gb_gates.w cc -g -I$INCLUDEDIR -c gb_gates.c ctangle gb_lisa.w cc -g -I$INCLUDEDIR -c gb_lisa.c ctangle gb_miles.w cc -g -I$INCLUDEDIR -c gb_miles.c ctangle gb_plane.w cc -g -I$INCLUDEDIR -c gb_plane.c ctangle gb_raman.w cc -g -I$INCLUDEDIR -c gb_raman.c ctangle gb_rand.w cc -g -I$INCLUDEDIR -c gb_rand.c ctangle gb_roget.w cc -g -I$INCLUDEDIR -c gb_roget.c ctangle gb_words.w cc -g -I$INCLUDEDIR -c gb_words.c ctangle gb_dijk.w cc -g -I$INCLUDEDIR -c gb_dijk.c ctangle gb_save.w cc -g -I$INCLUDEDIR -c gb_save.c rm -rf certified ar rcv libgb.a gb_flip.o gb_graph.o gb_io.o gb_sort.o gb_basic.o gb_books.o \ gb_econ.o gb_games.o gb_gates.o gb_lisa.o gb_miles.o gb_plane.o gb_raman.o \ gb_rand.o gb_roget.o gb_words.o gb_dijk.o gb_save.o ranlib libgb.a ctangle test_sample.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o test_sample test_sample.c -lgb test_sample > sample.out diff test.gb test.correct diff sample.out sample.correct rm test.gb sample.out test_io test_graph test_flip test_sample echo "Congratulations --- the tests have all been passed." touch certified mkdir $SGBDIR mkdir $DATADIR cp -p anna.dat david.dat econ.dat games.dat homer.dat huck.dat jean.dat \ lisa.dat miles.dat roget.dat words.dat $DATADIR mkdir $LIBDIR cp libgb.a $LIBDIR mkdir $CWEBINPUTS cp -p boilerplate.w gb_types.w $CWEBINPUTS mkdir $INCLUDEDIR cp -p gb_flip.h gb_graph.h gb_io.h gb_sort.h gb_basic.h gb_books.h gb_econ.h \ gb_games.h gb_gates.h gb_lisa.h gb_miles.h gb_plane.h gb_raman.h gb_rand.h \ gb_roget.h gb_words.h gb_dijk.h gb_save.h Makefile $INCLUDEDIR Here "ctangle foo" is actually an abbreviation for the shell command "if test -r foo.ch; then ctangle foo.w foo.ch; else ctangle foo; fi" which supplies a change file to ctangle if you have prepared one. (The actions following "touch certified" are those of "make install", assuming that "make tests" was done first; these are the only actions that may need to be done as superuser. It's generally best not to be superuser until AFTER the tests have been passed; otherwise who knows what might happen?) If you want to install all the demonstration programs as well as the GraphBase library, say "make installdemos" after "make install". This causes the following sequence of actions to occur: ctangle assign_lisa.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o assign_lisa assign_lisa.c -lgb make book_components.c ctangle book_components.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o book_components book_components.c -lgb ctangle econ_order.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o econ_order econ_order.c -lgb ctangle football.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o football football.c -lgb ctangle girth.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o girth girth.c -lgb ctangle ladders.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o ladders ladders.c -lgb ctangle miles_span.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o miles_span miles_span.c -lgb ctangle multiply.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o multiply multiply.c -lgb ctangle queen.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o queen queen.c -lgb ctangle roget_components.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o roget_components roget_components.c -lgb ctangle take_risc.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o take_risc take_risc.c -lgb ctangle word_components.w cc -g -I$INCLUDEDIR -L. -L$LIBDIR -o word_components word_components.c -lgb mkdir $BINDIR mv assign_lisa book_components econ_order football girth ladders miles_span \ multiply queen roget_components take_risc word_components $BINDIR Complete instructions appear in the book by D. E. Knuth entitled The Stanford GraphBase: A Platform for Combinatorial Computing published jointly by ACM Press and Addison-Wesley (1993), ISBN 0-201-54275-7. IF ALL ELSE FAILS send trouble reports to sgb@cs.stanford.edu. IF YOU LIKE THE STANFORD GRAPHBASE send thanks to sgb@cs.stanford.edu. ******LATE-BREAKING NEWS: * The master sources at ftp.cs.stanford.edu contain all the files listed above (uncompressed), as well as a compressed file sgb.tar.gz that generates them on a UNIX system if you say "zcat sgb.tar.gz | tar xvpf -" using GNU's excellent new compression/decompression scheme. * The master sources also contain an ERRATA file listing all known errors in the GraphBase book. (A reward of $2.56 is paid to the first finder of an error; the errors listed in ERRATA are no longer worth anything.) * Although several of the GraphBase programs have changed since the system was first released, only one of those changes has affected the generated graphs. (This was an embarrassing correction to gb_rand.w, noted in the errata for page 388; it affects some instances of random_graph and random_bigraph, which therefore are no longer identical to the graphs of the same identifier obtained before June 1999.) Otherwise, corrections were only made to improve comments or to remove anomalies in cases where some compilers had difficulty. * The demonstration programs sometimes return a negative value to the operating system environment. For example, an error message about improper "Usage:" is often followed by "return -2". The actual number received by a shell script or makefile running such programs will differ on different operating systems (and in particular a negative number will often be converted to a nonnegative 8-bit integer). The returned values have no great importance; they are intended only for debugging. * It is planned to have subdirectories that contain change files for systems that are particularly hard to accommodate. For example, a "DOS" subdirectory might be provided for a certain well-known operating system. We will try to give UPPERCASE names to such subdirectories so that they are easily spotted. * Important note: The Stanford GraphBase programs do not obey the ANSI C standard restriction on comparison of pointers. In fact, the author (Knuth) confesses to being unaware until recently that such a restriction was part of the standard; he wrote the code under the assumption that pointers were essentially machine addresses. No problem occurs with respect to |==| and |!=| comparison, but the code sometimes has a loop like |for (p=hi;p>=lo;p--)| where |lo| is the base address of a dynamically allocated array. Strictly speaking, |lo-1| is undefined. In other places (e.g., sections 23 and 26 of GB_SAVE) we explicitly test if one pointer is less than another; this code effectively sorts a set of pointers of unknown origin by magnitude, so it assumes that |<| defines a total ordering on pointers. In GB_GATES section 2 we cast a pointer to unsigned long and test whether the result is |<=1|; conversely, the constant 1 is read as a pointer via a union type in GB_SAVE section 10. None of this is likely to cause any trouble unless your environment has segmented architecture and 16-bit offsets within each segment. If you do have such a system, your best bet is probably to get one of the free and excellent ports of the GCC compiler. For example, DJ Delorie has succeeded in porting GCC to the MSDOS environment. Alternatively, a set of change files appears on directory sgb/ANSI. * The code also assumes throughout that NULL is equivalent to zero (for example, that pointer arrays delivered by |calloc| are full of NULLs). It would be almost impossible to remove this assumption; don't even think about it. ���������������������������������������������������������������������������������������abstract.plaintex�����������������������������������������������������������������������������������0000444�0001750�0001750�00000026375�06215170170�012563� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% EXTENDED ABSTRACT DESCRIBING THE STANFORD GRAPHBASE \magnification\magstep1 \advance\vsize by 1.5\baselineskip \parskip3pt plus 1pt \font\sc=cmcsc10 \def\disleft#1:#2:#3\par{\par\hangindent#1\noindent \hbox to #1{#2 \hfill \hskip .1em}\ignorespaces#3\par} \def\TeX{T\hbox{\hskip-.1667em\lower.424ex\hbox{E}\hskip-.125em X}} \def\biba{\par\parindent 40pt\hangindent 60pt} \centerline{\bf The Stanford GraphBase: A Platform for Combinatorial Computing} \smallskip \centerline{\sl Donald E. Knuth, Stanford University} \noindent A highly portable collection of programs and data is now available to researchers who study combinatorial algorithms and data structures. All files are in the public domain and usable with only one restriction: They must not be changed! A~``change file'' mechanism allows local customization while the master files stay intact. The programs are intended to be interesting in themselves as examples of ``literate programming.'' Thus, the Stanford GraphBase can also be regarded as a collection of approximately 30 essays for programmers to enjoy reading, whether or not they are doing algorithmic research. The programs are written in {\tt CWEB}, a~combination of \TeX\ and~C that is easy to use by anyone who knows those languages and easy to read by anyone familiar with the rudiments of~C. (The {\tt CWEB} system is itself portable and in the public domain.) Four program modules constitute the {\it kernel\/} of the GraphBase: { \biba {\sc gb\_$\,$flip} is a portable random number generator; \biba {\sc gb\_$\,$graph} defines standard data structures for graphs and includes routines for storage allocation; \biba {\sc gb\_$\,$io} reads data files and makes sure they are uncorrupted; \biba {\sc gb\_$\,$sort} is a portable sorting routine for 32-bit keys in linked lists of nodes. } \noindent All of the other programs rely on {\sc gb\_$\,$graph} and some subset of the other three parts of the kernel. A dozen or so {\it generator modules\/} construct graphs that are of special interest in algorithmic studies. For example, {\sc gb\_$\,$basic} contains 12~subroutines to produce standard graphs, such as the graphs of queen moves on $d$-dimensional rectangular boards with ``wrap-around'' on selected coordinates. Another generator module, {\sc gb\_$\,$rand}, produces several varieties of random graphs. Each graph has a unique identifier that allows researchers all over the world to work with exactly the same graphs, even when those graphs are ``random.'' Repeatable experiments and standard benchmarks will therefore be possible and widely available. Most of the generator modules make use of {\it data sets}, which the author has been collecting for 20~years in an attempt to provide interesting and instructive examples for some forthcoming books on combinatorial algorithms ({\sl The Art of Computer Programming}, Volumes 4A, 4B, and~4C). For example, one of the data sets is {\tt words.dat}, a~collection of 5-letter words of English that the author believes is ``complete'' from his own reading experience. Each word is accompanied by frequency counts in various standard corpuses of text, so that the most common terms can be singled out if desired. {\sc gb\_$\,$words} makes a subset of words into a graph by saying that two words are adjacent when they agree in~4 out of~5 positions. Thus, we can get from {\tt words} to {\tt graph} in seven steps: \disleft 30pt:: {\tt words, wolds, golds, goads, grads, grade, grape, graph.} \noindent This is in fact the shortest such chain obtainable from {\tt words.dat}. A dozen or so {\it demonstration modules\/} are also provided, as illustrations of how the generated graphs can be used. For example, the {\sc ladders} module is an interactive program to construct chains of 5-letter words like the one just exhibited, using arbitrary subsets of the data. If we insist on restricting our choices to the 2000 most common words, instead of using the entire collection of about 5700, the shortest path from {\tt words} to {\tt graph} turns out to have length~20: \disleft 30pt:: {\tt words, lords, loads, leads, leaps, leapt, least,} \vskip-5pt \disleft 30pt:: {\tt lease, cease, chase, chose, chore, shore, shone,} \vskip-5pt \disleft 30pt:: {\tt phone, prone, prove, grove, grave, grape, graph.} Several variations on this theme have also been implemented. If we consider the distance between adjacent words to be alphabetic distance, for example, the shortest path from {\tt words} to {\tt graph} turns out to be \disleft 30pt:: {\tt words} (3) {\tt woods} (16) {\tt goods} (14) {\tt goads} (3) {\tt grads} (14) {\tt grade} (12) {\tt grape} (3) {\tt graph}, \noindent total length 65. The {\tt LADDERS} module makes use of another GraphBase module called {\sc gb\_$\,$dijk}, which carries out Dijkstra's algorithm for shortest paths and allows the user to plug in arbitrary implementations of priority queues so that the performance of different queuing methods can be compared. The graphs produced by {\sc gb\_$\,$words} are undirected. Other generator modules, like {\sc gb\_$\,$roget}, produce directed graphs. Roget's famous {\sl Thesaurus\/} of 1882 classified all concepts into 1022 categories, which we can call the vertices of a graph; an arc goes from~$u$ to~$v$ when category~$u$ contains a cross reference to category~$v$ in Roget's book. A~demonstration module called {\sc roget\_$\,$components} determines the strong components of graphs generated by {\sc gb\_$\,$roget}. This program is an exposition of Tarjan's algorithm for strong components and topological sorting of directed graphs. Similarly, world literature leads to further interesting families of undirected graphs via the {\sc gb\_$\,$books} module. Five data sets {\tt anna.dat}, {\tt david.dat}, {\tt homer.dat}, {\tt huck.dat}, and {\tt jean.dat} give information about {\sl Anna Karenina}, {\sl David Copperfield}, {\sl The Iliad}, {\sl Huckleberry Finn}, and {\sl Les Mis\'erables}. As you might expect, the characters of each work become the vertices of a graph. Two vertices are adjacent if the corresponding characters encounter each other, in selected chapters of the book. A~demonstration program called {\sc book\_$\,$components} finds the blocks (i.e., biconnected components) of these graphs using the elegant algorithm of Hopcroft and Tarjan. Another module, {\sc gb\_$\,$games}, generates graphs based on college football scores. All the games from the 1990 season between America's leading 120 teams are recorded in {\tt games.dat}; this data leads to ``cliquey'' graphs, because most of the teams belong to leagues and they play every other team in their league. The overall graph is, however, connected. A~demonstration module called {\sc football} finds long chains of scores, to prove for instance that Stanford might have trounced Harvard by more than 2000 points if the two teams had met---because Stanford beat Notre Dame by~5, and Notre Dame beat Air Force by~30, and Air Force beat Hawaii by~24, and \dots~, and Yale beat Harvard by~15. (Conversely, a~similar ``proof'' also ranks Harvard over Stanford by more than 2000 points.) No good algorithm is known for finding the optimum solution to problems like this, so the data provides an opportunity for researchers to exhibit better and better solutions with better and better techniques as algorithmic progress is made. The {\sc gb\_$\,$econ} module generates directed graphs based on the flow of money between industries in the US economy. A~variety of graphs can be obtained, as the economy can be divided into any number of sectors from~2 to~79 in this model. A~demonstration program {\sc econ\_$\,$order} attempts to rank the sectors in order from ``suppliers'' to ``consumers,'' namely to permute rows and columns of a matrix so as to minimize the sum of entries above the diagonal. A reasonably efficient algorithm for this problem is known, but it is very complicated. Two heuristics are implemented for comparison, one ``greedy'' and the other ``cautious.'' Greed appears to be victorious, at least in the economic sphere. The highway mileage between 128 North American cities appears in {\tt miles.dat}, and the {\sc gb\_$\,$miles} module generates a variety of graphs from~it. Of special interest is a demonstration module called {\sc miles\_$\,$span}, which computes the minimum spanning trees of graphs output by {\sc gb\_$\,$miles}. Four algorithms for minimum spanning trees are implemented and compared, including some that are theoretically appealing but do not seem to fare so well in practice. An approach to comparison of algorithms called ``mem counting'' is shown in this demonstration to be an easily implemented machine-independent measure of efficiency that gives a reasonably fair comparison between competing techniques. A generator module called {\sc gb\_$\,$raman} produces ``Ramanujan graphs,'' which are important because of their role as expander graphs, useful for communication. A~demonstration module called {\sc girth} computes the shortest circuit and the diameter of Ramanujan graphs. Notice that some graphs, like those produced by {\sc gb\_$\,$basic} or {\sc gb\_$\,$raman}, have a rigid mathematical structure; others, like those produced by {\sc gb\_$\,$roget} or {\sc gb\_$\,$miles}, are more ``organic'' in nature. It is interesting and important to test algorithms on both kinds of graphs, in order to see if there is any significant difference in performance. A generator module called {\sc gb\_$\,$gates} produces graphs of logic circuits. One such family of graphs is equivalent to a simple RISC chip, a~programmable microcomputer with a variable number of registers. Using such a ``meta-network'' of gates, algorithms for design automation can be tested for a range of varying parameters. A~demonstration module {\sc take\_$\,$risc} simulates the execution of the chip on a sample program. Another meta-network of gates will perform parallel multiplication of $m$-bit numbers by $n$-bit numbers or by an $n$-bit constant; the {\sc multiply} module demonstrates these circuits. Planar graphs are generated by {\sc gb\_$\,$plane}, which includes among other things an implementation of the best currently known algorithm for Delaunay triangulation. Pixel data can lead to interesting bipartite graphs. Leonardo's {\sl Gioconda\/} is represented by {\tt lisa.dat}, an array of pixels that is converted into graphs of different kinds by {\sc gb\_$\,$lisa}. A~demonstration routine {\sc assign\_$\,$lisa} solves the assignment problem by choosing one pixel in each row and in each column so that the total brightness of selected pixels is maximized. Although the assignment problem being solved here has no relevance to art criticism or art appreciation, it does have great pedagogical value, because there is probably no better way to understand the characteristics of a large array of numbers than to perceive the array as an image. A~module called {\sc gb\_$\,$save} converts GraphBase graphs to and from an ASCII format that readily interfaces with other systems for graph manipulation. For further information see {\sl The Stanford GraphBase}, published by ACM Press in 1993. The book could also be called ``Fun and games with the Stanford GraphBase,'' because the demonstration programs are great toys to play with. Indeed, the author firmly believes that the best serious work is also good fun. We needn't apologize if we enjoy doing research.\looseness-1 \bye �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������cities.texmap���������������������������������������������������������������������������������������0000444�0001750�0001750�00000014034�05720721063�011702� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������% Plain TeX code to generate a map of the 128 cities in miles.dat % Parameters to the \\ macro are: % #1: city name to be typeset % #2,#3: (x,y) coordinates for lower left corner of label box % #4,#5: (x,y) coordinates for the dot % (#4,#5)=(12318-lng,1.5(lat-2672)), where (lat,lng) are the coords in miles.dat % Parameters to \| are similar, but the state code is separated out % and put on a second line, #3 ems from the right edge of the main line % e.g. City Name, ST .5(cityx,cityy,dotx,doty) % puts "ST" on the second line, .5em from the right \newdimen\unit \unit=.0012in \setbox0=\hbox{\fivesy\char15} \newbox\dotbox \setbox\dotbox=\hbox{\kern-.5\wd0\fivesy\char15} \wd\dotbox=0pt \newdimen\fudge \setbox0=\hbox{\fiverm p} \fudge=-\dp0 \advance\fudge-.5\ht\dotbox \advance\fudge.5\dp\dotbox \newdimen\vertpos \def\\#1 (#2,#3,#4,#5){\vertpos=#5\unit \advance\vertpos\fudge \setbox0=\hbox{\kern#4\unit \raise\vertpos\copy\dotbox}\wd0=0pt \box0 \setbox0=\hbox{\kern#2\unit \raise#3\unit\hbox{\fiverm#1}}\wd0=0pt \box0} \def\|#1, #2 #3(#4,#5,#6,#7){\vertpos=#7\unit \advance\vertpos\fudge \setbox0=\hbox{\kern#6\unit \raise\vertpos\copy\dotbox}\wd0=0pt \box0 \setbox0=\hbox{\kern#4\unit \raise#5\unit \hbox{\fiverm#1,\llap{\lower55\unit\hbox{#2\kern#3em}}}}\wd0=0pt \box0} \vglue 2in \noindent \\Ravenna, OH (3707,2103,4188,2166) \\Reading, PA (4735,2050,4719,2041) \\Red Bluff, CA (-415,2034,88,2019) \\Regina, SK (1535,3575,1847,3555) \\Reno, NV (306,1940,331,1920) \\Rhinelander, WI (3222,2860,3370,2838) \\Richfield, UT (877,1737,1103,1807) \|Richmond, IN .5(3620,1901,3823,1966) \\Richmond, VA (4585,1572,4567,1623) \\Roanoke, VA (4136,1514,4318,1582) \\Rochester, MN (2696,2613,3066,2595) \\Rochester, NY (4032,2445,4551,2466) \\Rockford, IL (2953,2277,3402,2332) \\Rock Springs, WY (1014,2161,1389,2230) \\Rocky Mount, NC (4548,1323,4532,1383) \\Roswell, NM (1410,938,1859,1002) \\Rutland, VT (5044,2504,5015,2533) \\Sacramento, CA (168,1796,163,1780) \\Saginaw, MI (3533,2432,3918,2506) \\Saint Augustine, FL (3996,402,4180,475) \\Saint Cloud, MN (2349,2837,2895,2827) \\Saint Johnsbury, VT (4991,2677,5110,2655) \\Saint Joseph, MI (3659,2319,3664,2307) \\Saint Joseph, MO (2203,1964,2828,1957) \|Saint Louis, MO .5(3105,1715,3293,1785) \\Saint Paul, MN (2776,2750,3002,2734) \\Salem, OR (-362,2665,9,2733) \\Salida, CO (1404,1791,1712,1771) \\Salina, KS (2162,1793,2551,1818) \\Salinas, CA (-261,1426,147,1492) \\Salisbury, MD (4771,1690,4752,1747) \\Salt Lake City, UT (620,2036,1124,2106) \\San Angelo, TX (1699,683,2268,711) \\San Antonio, TX (1855,377,2462,405) \\San Bernardino, CA (605,1090,581,1108) \\San Diego, CA (340,827,597,898) \\Sandusky, OH (3537,2158,4041,2209) \\San Francisco, CA (-580,1605,70,1659) \\San Jos\'e, CA (147,1541,124,1593) \\Santa Ana, CA (-18,1004,525,1056) \\Santa Barbara, CA (-272,1175,342,1155) \\Santa Fe, NM (1224,1284,1717,1344) \\Santa Rosa, CA (-531,1729,40,1758) \\Sarasota, FL (3604,29,4059,93) \\Sault Sainte Marie, MI (3894,2969,3877,2965) \\Savannah, GA (4223,804,4203,804) \\Schenectady, NY (4942,2380,4917,2415) \\Scottsbluff, NE (1514,2292,1946,2272) \|Scranton, PA 1(4619,2245,4745,2203) \\Seattle, WA (-343,3143,79,3132) \\Sedalia, MO (2599,1720,2989,1798) \\Selma, AL (3630,786,3610,855) \\Seminole, OK (2416,1206,2644,1276) \\Sheridan, WY (1204,2733,1616,2712) \\Sherman, TX (2180,969,2651,1038) \\Shreveport, LA (2845,887,2937,868) \\Sioux City, IA (2185,2296,2673,2365) \\Sioux Falls, SD (2102,2535,2639,2523) \\South Bend, IN (3118,2210,3687,2244) \\Spokane, WA (579,3153,571,3142) \\Springfield, IL (2896,1977,3347,1962) \\Springfield, MA (5070,2256,5053,2307) \\Springfield, MO (2986,1507,2983,1575) \\Springfield, OH (3430,1990,3931,1980) \\Staunton, VA (4151,1644,4405,1714) \\Sterling, CO (1767,2105,1990,2085) \\Steubenville, OH (3646,2045,4250,2046) \\Stevens Point, WI (3288,2600,3355,2670) \\Stockton, CA (204,1658,183,1686) \\Stroudsburg, PA (4818,2111,4793,2140) \\Sumter, SC (4292,1007,4277,1080) \\Swainsboro, GA (4087,894,4078,882) \\Syracuse, NY (4300,2374,4697,2449) \\Tacoma, WA (-380,3019,69,3078) \\Tallahassee, FL (3334,502,3884,559) \\Tampa, FL (4070,202,4067,184) \\Terre Haute, IN (3008,1855,3571,1912) \\Texarkana, TX (2923,994,2907,1006) \\Toledo, OH (3743,2257,3958,2239) \\Topeka, KS (2380,1869,2745,1849) \\Toronto, ON (4065,2561,4374,2539) \\Traverse City, MI (3775,2682,3749,2706) \\Trenton, NJ (4859,1976,4835,2026) \\Trinidad, CO (1740,1501,1861,1567) \\Tucson, AZ (1018,755,1215,825) \\Tulsa, OK (2562,1436,2721,1416) \\Tupelo, MS (3037,1144,3441,1131) \\Tuscaloosa, AL (3560,986,3555,973) \\Twin Falls, ID (361,2393,865,2376) \\Tyler, TX (2417,816,2782,844) \|Uniontown, PA 1(4295,1986,4339,1977) \\Utica, NY (4807,2438,4789,2458) \\Valdosta, GA (3995,555,3984,616) \\Valley City, ND (2037,3050,2511,3030) \\Vancouver, BC (-257,3403,0,3382) \\Vicksburg, MS (2963,771,3224,844) \\Victoria, TX (2518,242,2611,313) \\Vincennes, IN (3575,1737,3559,1794) \\Waco, TX (2494,652,2598,724) \\Walla Walla, WA (370,2830,479,2902) \|Warren, PA .5(4285,2295,4398,2269) \\Washington, DC (4633,1807,4609,1825) \\Waterbury, CT (5027,2173,5007,2224) \\Waterloo, IA (2698,2383,3078,2367) \\Watertown, NY (4731,2571,4720,2589) \\Watertown, SD (2056,2701,2601,2727) \\Waukegan, IL (3180,2361,3529,2346) \\Wausau, WI (3363,2741,3348,2736) \\Waycross, GA (4087,675,4077,675) \\Weed, CA (-252,2225,73,2205) \\Wenatchee, WA (291,3040,280,3105) \\West Palm Beach, FL (4190,-70,4307,0) \|Wheeling, WV 1(3975,1935,4240,2002) \\Wichita, KS (2286,1575,2578,1645) \\Wichita Falls, TX (1909,1098,2463,1077) \|Williamson, WV .5(3650,1616,4084,1644) \|Williamsport, PA .5(4168,2192,4612,2179) \\Williston, ND (1471,3232,1950,3214) \\Wilmington, DE (4776,1899,4757,1954) \\Wilmington, NC (4521,1143,4520,1128) \|Winchester, VA 0(4135,1805,4496,1870) \\Winnipeg, MB (2347,3495,2597,3474) \\Winston-Salem, NC (3586,1354,4287,1407) \\Wisconsin Dells, WI (2769,2469,3335,2536) \\Worcester, MA (5140,2320,5132,2332) \\Yakima, WA (-11,2912,261,2982) \\Yankton, SD (2110,2410,2573,2424) \|Youngstown, OH 1(4262,2092,4247,2157) \nopagenumbers\bye ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������blank.w���������������������������������������������������������������������������������������������0000444�0001750�0001750�00000000204�05434004530�010446� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@i gb_types.w \datethis @* Introduction. @p #include "gb_graph.h" /* the GraphBase data structures */ @h@# main() { } @* Index. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������sample.correct��������������������������������������������������������������������������������������0000444�0001750�0001750�00000010270�06757372100�012051� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������GraphBase samples generated by test_sample: "raman(31,3,3,4)" 12 vertices, 96 arcs, util_types ZZZIIIIZZZZZZZ V4: "(1,0;1,1)"[1][0][1] ->"(1,2;1,0)"[1][2][0], 1[16] ->"(1,1;1,2)"[1][1][2], 1[17] ->"(0,2;1,0)"[0][2][0], 1[18] ->"(1,1;0,1)"[1][1][3], 1[19] ->"(2,0;1,2)"[2][0][2], 1[20] ->"(1,0;0,1)"[1][0][3], 1[21] ->"(0,2;1,1)"[0][2][1], 1[22] ->"(2,1;1,1)"[2][1][1], 1[23] "board(1,1,2,-33,1,-2147483648,1)" 2048 vertices, 14336 arcs, util_types ZZZIIIZZZZZZZZ V2000: "0.0.1.0.0.1.0.0.1.0.0.1.0.0.1.0.0.0.0.0.1.0.0.0.0.0.0.0.0.0.0.0.0"[0][0][1] ->"0.0.1.0.0.1.0.0.1.0.0.1.0.0.1.0.0.1.0.0.1.0.0.0.0.0.0.0.0.0.0.0.0"[0][0][1], 1 ->"0.0.1.0.0.1.0.0.1.0.0.1.0.0.1.0.0.0.0.0.1.0.0.1.0.0.0.0.0.0.0.0.0"[0][0][1], 1 ->"0.0.1.0.0.1.0.0.1.0.0.1.0.0.1.0.0.0.0.0.1.0.0.0.0.0.1.0.0.0.0.0.0"[0][0][1], 1 ->"0.0.1.0.0.1.0.0.1.0.0.1.0.0.1.0.0.0.0.0.1.0.0.0.0.0.0.0.0.1.0.0.0"[0][0][1], 1 ->"0.0.1.0.0.1.0.0.1.0.0.1.0.0.1.0.0.0.0.0.1.0.0.0.0.0.0.0.0.0.0.0.0"[0][0][1], 1 ->"0.0.1.0.0.1.0.0.1.0.0.1.0.0.1.0.0.0.0.0.1.0.0.0.0.0.0.0.0.0.0.0.1"[0][0][1], 1 "subsets(32,18,16,0,0,0,0x80000000,1)" 3 vertices, 2 arcs, util_types ZZZIIIZZZZZZZZ V1: "17.15"[17][15][0] ->"18.14"[18][14][0], 1 "gunion(random_lengths(complement(random_graph(3,10,1,1,0,0,dist,1,2,1),1,1,0),0,10,12,dist,2),random_graph(3,10,1,1,0,0,dist,1,2,1),1,0)" 3 vertices, 30 arcs, util_types ZZZZZZZZZZZZZZ V2: "2" ->"1", 1 ->"1", 2 ->"1", 2 ->"1", 10 ->"0", 2 ->"0", 11 "partial_gates(risc(16),1,43210,98765)" 1702 vertices, 3796 arcs, util_types ZZZIIVZZZZZZZA[->"Z1508"[0][38]] V79: "R10:10"[0][76]["Z898"[0][38]] "book("homer",500,400,2,12,10000,-123456,789)" 100 vertices, 4 arcs, util_types IZZIISIZZZZZZZ V81: "Eetion"[90][2][1]["king of Cilicia, father of AH"] ->"Andromache"[377][2][1]["wife of HT"], 1[6] "econ(40,0,400,-111)" 40 vertices, 512 arcs, util_types ZZZZIAIZZZZZZZ V11: "Printing and publishing"[69451][->NULL] ->"Food, liquor, and candy"[300724], 1[1863] ->"Cigarettes, cigars, tobacco"[24445], 1[195] ->"Printing and publishing"[69451], 1[6089] ->"Business support services"[463594], 1[8369] ->"Personal services"[827615], 1[9073] ->"Users"[3999362], 1[30676] "games(60,70,80,-90,-101,60,128,999999999)" 60 vertices, 114 arcs, util_types IIZSSSIIZZZZZZ V14: "Maryland"[2752512][131072]["MD"]["Terps"]["Atlantic Coast"] ->"Louisiana Tech"[0][0]["LTECH"]["Bulldogs"]["(null)"], 34[2][111] ->"Virginia"[65863868][17825857]["VA"]["Cavaliers"]["Atlantic Coast"], 35[1][83] "miles(50,-500,100,1,500,5,314159)" 50 vertices, 164 arcs, util_types ZZIIIIZZZZZZZZ V20: "Saint Louis, MO"[453085][3293][1785][24] ->"Tupelo, MS"[23905][3441][1131][86], 364 ->"Springfield, MO"[133116][2983][1575][62], 235 "plane_lisa(100,100,50,1,300,1,200,2975050,11900200)" 2452 vertices, 10814 arcs, util_types ZZZIIIZZIIZZZZ[100][100] V1294: "1294"[11][2407][2408] ->"1295"[12][2409][2409], 1 ->"1256"[8][2308][2308], 1 ->"1293"[10][2406][2508], 1 ->"1255"[10][2307][2307], 1 "plane_miles(50,500,-100,1,1,40000,271818)" 51 vertices, 96 arcs, util_types ZZIIIIZZZZZZZZ V14: "Saint Louis, MO"[453085][3293][1785][24] ->"Waterloo, IA"[75985][3078][2367][103], 373 ->"South Bend, IN"[109727][3687][2244][58], 358 ->"San Diego, CA"[875538][597][898][35], 1875 "random_bigraph(300,3,1000,-1,0,dist,-500,500,666)" 303 vertices, 1138 arcs, util_types ZZZZZZZZIZZZZZ[300] V3: "3" ->"300", -386 ->"302", 180 "roget(1000,3,1009,1009)" 1000 vertices, 3573 arcs, util_types IZZZZZZZZZZZZZ V40: "thought"[461] ->"imagination"[527], 1 ->"memory"[517], 1 ->"inquiry"[471], 1 ->"inattention"[468], 1 ->"attention"[467], 1 Ooops, we just ran into panic code 30! "words(90,{100,-80588,50000,18935,-18935,18935,18935,18935,18935},70000000,69)" 90 vertices, 38 arcs, util_types IZZZZZIZZZZZZZ V5: "would"[590131605] ->"world"[150515830], 1[2] ->"could"[438944820], 1[0] "words(5757,0,0,69)" 5757 vertices, 28270 arcs, util_types IZZZZZIZZZZZZZ V5555: "laded"[0] ->"lades"[0], 1[4] ->"laden"[204], 1[4] ->"laved"[0], 1[2] ->"lased"[0], 1[2] ->"lazed"[2], 1[2] ->"lamed"[112], 1[2] ->"laced"[168], 1[2] ->"jaded"[110], 1[0] ->"waded"[240], 1[0] ->"faded"[430], 1[0] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test.correct����������������������������������������������������������������������������������������0000444�0001750�0001750�00000001511�05216535624�011546� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������* GraphBase graph (util_types ZZZZZZZZZZVZZZ,8V,102A) "complement(random_graph(3,10,1,1,0,0,dist,1,2,1),1,1,0)",3,10,V7 * Vertices "0",A4 "1",A8 "2",A9 "",0 "",0 "",0 "",0 "Testing",0 * Arcs V0,A1,1 V0,0,1 V1,A0,1 V0,0,1 V2,A2,1 V0,0,1 V1,A7,1 V1,A3,1 V2,A6,1 V1,A5,1 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 0,0,0 * Checksum 761246749 ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������test.dat��������������������������������������������������������������������������������������������0000444�0001750�0001750�00000000605�05407370017�010653� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������* File "test.dat" from the Stanford GraphBase (C) 1993 Stanford University * A test program used to validate the gb_io module (at least in part) * This file may be freely copied but please do not change it in any way! * (Checksum parameters 3,1008816584) 0000000000000000000000000000000000000000000000000000000000000000123456789ABCDEF Oops:(intentional mistake) * End of file "test.dat" ���������������������������������������������������������������������������������������������������������������������������+The+Stanford+GraphBase+����������������������������������������������������������������������������0000444�0001750�0001750�00000000000�05260073724�013366� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ERRATA����������������������������������������������������������������������������������������������0000444�0001750�0001750�00000032554�10601665152�010112� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������I had promised that the *.dat files would never change, but I learned later that miles.dat as originally distributed used the wrong two-letter state codes for Nebraska and Saskatchewan. This was politically incorrect. I decided to change to the correct codes, since this won't affect any of the data that's critical for the graph structure. Please replace your copies of miles.dat (and cities.texmap) if they are dated earlier than 1995. Similarly, I found an embarrassing typo ("Monsiuer" for "Monsieur") in the file jean.dat; and there was a spurious white pixel at the very bottom right corner of lisa.dat; those glitches were corrected in March 2007. ---------------------------------------- Here is a list of all significant changes that appeared in the third printing of The Stanford GraphBase book (2002). Corrections marked with * were already made in the second printing (1994); see the webpage http://www-cs-faculty.stanford.edu/~knuth/sgb.html for corrections to any subsequent printings. If you know of an error not listed here or there, chances are good that you can earn a reward by reporting it to sgb@cs.stanford.edu. [Note that comparison between pointers from differently calloc'd regions of memory is not considered erroneous; see the README file.] Many thanks to the readers who reported these glitches. Page 1, line 17: should say The eleven data files *.dat occupy 355,156 bytes Page 7, line -11: change "rough, black, empty" to "chump, given, flaky" Page 7, line -8: change "alphabet." to "alphabet. One solution, namely {murky, qophs, vixen, waltz}, hit 14 of the last 16." *Page 10, line 9: should say John Lewis Roget *Page 14, line 6: change "it is" to "is" Page 20, line 1: change "&" to "\&" Page 21, lines 16, 15, 2, and -1: change 11519, 17090, 14650, 23098 to 11736, 17770, 14919, 23901, respectively *Pages 22 and 24: the illustrations are too low on the page Page 22: should say Regina, SK and Scottsbluff, NE Page 23, lines 6 and 7: change 91256 to 91953 and 274127 to 290169 Page 26, insert at beginning of line 17: (In rare cases, the triangle inequality will fail.) *Page 26, line 17: $2^14$ is, of course, 16384 Page 28, line 2: should say "is much more difficult than the" *Pages 29 and 30: change "dominos" to "dominoes" (7 times) Page 33, line 14: insert thin space before "..." *Page 34: remove extra comma in the caption to Figure 11 Page 34, caption to Figure 12: change "(28,4,8,4,0,0)" to "(8,4,8,4,0,0,0)" *Page 40: remove the first "poset," on line 19 Page 43, line 5: change "1982 and 1984" to "1984 and 1986" Page 43, line 7: change "1988" to "1989" Page 45, line 12: change "under under" to "under" Page 45, line 23: change "279" to "239" Page 46, line 4 from the bottom: industry $j$, not industry $k$ Page 52, line 5: change "the vertices" to "two vertices" *Page 58: numbered line 61 should end "+The+Stanford+GraphBase+" *Page 58: numbered lines 69 and 70 should end with double quotes *Page 59: insert change "test" to "./test" on numbered lines 87, 88, 89, 93 Page 60: change "-o foo" to "-o foo foo.c" in both displayed command lines Page 60, line 7 from bottom: change "/usr" to "`/usr" Page 61, line 5 from bottom: change "CHANGEFILE" to "CHANGEFILES" Page 62: line 25: change "lines 92--96" to "lines 73--77" Page 63, line 15: change "gb_graph.o" to "gb_graph.o," Page 75: remove semicolon at end of line 2 Page 78, line 8: The accent in K\"onig should actually be the long Hungarian accent, K\H{o}nig. (Also on line 6 from the bottom, and on page 79 lines 1 and 11, and in the index on page 573.) Page 78: the long-double-dash notation used on lines 15 and following is changed to a heavy bar Page 78: in display (*) and the following lines, use notation like $r^{(0)$ and $c^{(1)}$ instead of $r_0$ and $c_1$ Page 78, line 7 from bottom: change "matrix" to "matrix, of size $n\times n$" Page 81, line 14: change "row $l$" to "column $l$" *Page 90, lines -10 and -9: remove the semicolons *Page 99, line 6: change "x --- y" to "y --- z" *Page 121, line 4 should say: "assume that |BUF_SIZE| is rather large, but in cases of doubt they ensure that |BUF_SIZE| will never be exceeded." Page 124, line 11 from bottom: change "x_k+\delta_k" to "x_d+\delta_d" Page 138, line 15 should reverse the order of the tests: if (ss<UL_BITS && (size_bits&(((unsigned long)1)<<ss))) { Page 140, line 11: change "(1994), to appear" to "{\bf32} (1994), 115--144" Page 156, lines 16 and 17 should be replaced by the following: removed. Regardless of the value of |copy|, information that might have been present in the utility fields will not be copied, and arc lengths will all be set to 1. Page 160, line -5: change "panic(no_room+1)" to "panic(missing_operand)" Page 163, line 19: change "panic(no_room+1)" to "panic(missing_operand)" Page 168, line 9: change "panic(no_room+1)" to "panic(missing_operand)" Page 182, line -9: change "their weights" to "their statistics" Page 184, line -6: change "their weights" to "their statistics" Page 184, line -2: change "check sum" to "checksum" Page 194, line 15: change "awhile" to "a while" *Page 194, line -7: change "hh==NULL" to "!hh" (i.e., change $hh\equiv\Lambda$ to $\lnot hh$) Page 194, line 23: change "long dummy(v)" to "static long dummy(v)" Page 198, line 17: change "Vertex head[128]" to "static Vertex head[128]" Page 200, line 9: change "long master_key" to "static long master_key" Page 200, line 17: change "logical-anding" to "bitwise-anding" Page 213, line 5: change "obtaining" to "obtained" Page 216, lines 8 and 9: The period length of the generated numbers is $2^{85}-2^{30}$. Page 217, line 3: the recurrence should be $a_n=(a_{n-55}-a_{n-24})\bmod m$ Page 217, insert a new paragraph at the beginning of section 6: The numbers generated by |gb_next_rand()| seem to be satisfactory for most purposes, but they do fail a stringent test called the ``birthday spacings test,'' devised by George Marsaglia. [See, for example, {\sl Statistics and Probability Letters\/ \bf9} (1990), 35--39.] One way to get numbers that pass the birthday test is to discard half of the values, for example by changing `|gb_flip_cycle()|' to `|gb_flip_cycle(),gb_flip_cycle()|' in the definition of |gb_next_rand()|. Users who wish to make such a change should define their own substitute macro. Page 218, line 5: change "mod" to "divided by"; also change "logical-and" to "bitwise-and" Page 222, line 10 of section 2: change "copy" to "complement" Page 282, after #include <stdio.h>: also say #include <stdlib.h> (twice) *Page 287, insert a new paragraph after line 4, to wit: {\sl Important Note:\/} Programs of the Stanford GraphBase implicitly assume that all memory allocated by |calloc| comes from a single underlying memory array. Therefore pointer values are compared to each other in many places, even when the objects pointed to have been allocated at different times. Strictly speaking, this liberal use of pointer comparisons fails to conform to the restrictions of ANSI Standard \CEE/, when the comparison involves a less-than or greater-then relation. Users whose system supports only the strict standard will need to make several dozen changes. *Page 288, section 18, line 11: replace "g->uu.I++;" by the code { g->uu.I++; printf("."); fflush(stdout); } and make entries for fflush and stdout in the mini-index on page 289 Page 303, line 2: change "check sum" to "checksum" *Page 304, line 8: change "is none" to "if none" Page 304, line 20: change "81" to "sizeof(buffer)" Page 305, line 15: change 100 to |unexpected_char| Page 306, bottom line: change "icode[*p]" to "imap_ord(*p)" Page 308, replacement for 3rd line from bottom: icode[0]=d; /* make sure '\0' is a nondigit */ if (imap_ord(*cur_pos)<d) return icode[*cur_pos++]; Page 309, line 5: change "icode[*cur_pos]" to "imap_ord(*cur_pos)" Page 312, line 12: change "#endif DATA_DIRECTORY" to "#endif" Page 312, line 18: change "19" to "sizeof(file_name)-1" Page 312, line 9 from the bottom: change "5-letter" to "five-letter" *Page 312, bottom line: the comma should be in typewriter type Page 320, line 14: we want to compute $\langle 2a_0, a_0+a_1, 2a_1 \rangle$ Page 323, line 16: change "check sum" to "checksum" Page 330, line 17: change "util_types[6]" to "util_types[7]" *Page 332, line -7: change "decidegrees" to "centidegrees" Page 335, section 10: the variables should all be declared static (this affects the mini-indexes on pages 337, 339, 341) Page 336, lines 22 and 23: change "SA" to "SK" and delete the sentence about New Brunswick vs Nebraska (Nebraska changed from NB to NE long ago!) Page 346, line 14 (display): insert ")" before final comma Page 346, line 5 of section 9: the sentence should begin "The utility field |z_coord| must contain a unique ID number" Page 348, line 2, change "void new_euclid_edge(u,v)" to "static void new_euclid_edge(u,v)" Similarly, the declarations of int_sqrt (page 349), sign_test (page 350), ccw (page 352), incircle (page 353), ff, gg, hh, jj (page 355), flip (page 363), and new_mile_edge (page 365) should all be prefixed by "static" Page 352, line 7, change "that that" to "that" Page 354, section 23. The displayed formulas should define functions ff, gg, hh, and jj (not f, g, h, and j) *Page 366, section 2, line 6: should say {1,3,4,9,10,12} *Page 374: this commentary was not precisely correct; the revised version can be found in file gb_raman.w in the current sources Page 376, line 12: change "$p-a^2-2c^2$" to "$p-a^2-b^2-2c^2$" Page 386, line 21: change (25,m,...) to (31,m,...) Page 387, line 14: change "of of" to "of" Page 388, line 17: change "max_len-min_len" to "max_len-min_len+1" Page 400, section 7: the variables should both be declared static (this affects the mini-index on page 403) Page 410, replacement for the four last lines: 13. define buffer (&item_buf[MAX_SV_STRING+3]) /* the last 81 chars of |item_buf| */ <Private variables 8>= static char item_buf[MAX_SV_STRING+3+81]; /* an item to be output */ Page 413, line 10: change "output; in" to "output; the value $-3$ will be returned if memory is exhausted. In" Page 414, line 5: remove the semicolon Page 414, swap lines 7--8 with with lines 9--10 *Page 414: last line should end ", or if comparisons between pointers are undefined." Page 415: new line of code to follow line 12: if (blocks==NULL) return -3; /* out of memory */ Page 426, line 13: change "common words" to "common five-letter words" Page 430: the bottom few lines should move to page 431 Page 436, section 17: node_blocks should be declared static (this affects the mini-indexes on pages 433 and 439) Page 442, line 23: change "3,675,808" to "3,674,808" *Page 444, section 6, line 4: insert "n-vertex" before "regular" *Page 444, section 7, line 3: should be superscript g, not subscript Page 445, line -6: change "4*q*2" to "4*q*q" Pages 452 and 453: change "rand" to "randm" (six times) Page 456, lines 8 and 9 of section 18: change "u->arcs->len" to "(u->arcs-1)->len" and vice versa *Page 461, line 16: remove the semicolon Page 466, line 12 from the bottom: remove the semicolon Page 481, line 12: change "u->parent" to "o,u->parent" Page 490, line 19: remove the semicolon Page 494, line 5 of section 65: change "t->rsib" to "o,t->rsib" Page 495, line 5: change "o,k" to "oo,k" *Page 497, line 2: change "respect" to "respect to" Page 497, line 18: change "1000" to "100" Page 497, lines 16, 17, 29, 30: change "11519" to "11736", "17090" to "17770", "58544" to "59050", and "165749" to "175519" *Page 512, section 2, line 9: remove the semicolon *Page 537: add "GB_GRAPH 18," to the index listings for fflush Page 539: icode is not in GB_IO 17 Page 541: missing_operand is in GB_BASIC 81,87, 95 Page 542: no_room is not in GB_BASIC 81,87, 95 *Page 543: section numbers in the index listings for panic_code should not be underlined except in GB_GRAPH Page 544: rand becomes randm (and alphabetized later) *Page 545: add "GB_GRAPH 18," to the index listings for stdout Page 547: add section 10 to the index listings for unexpected_char in GB_IO Page 555: move the prototype for gb_number to page 554, where it belongs in alphabetic order Page 557: "all_parts(10)" should be "all_parts(10,0)" Page 558: "all_perms(5)" should be "all_perms(5,0)" Page 559: "book("homer",280,0,1,0,0,1,1,0)" should be "book("homer",280,0,0,0,1,1,0)" Page 561: "all_perms(7)" should be "all_perms(7,0)" Page 561, last two lines: change "multiple edges" to "multiple arcs" Page 562, lines 7 and 11: change "multiple edges" to "multiple arcs" Page 568, line 5: change ". 15" to ", 15" Pages 568 and 569: insert thin space after the comma when giving issue numbers, in references [5], [9], [15], [16], [23], [30], [31]. *Page 572: "Jarn{\'\i}, k" should be "Jarn{\'\i}k" Page 573: "K\"onig" should be "K\H{o}nig" Page 573: should say "literate programming" not "literate programmming" Page 573, Marsaglia, George, 217. Page 574: permutations, random, 104. *Page 574: should say Roget, John Lewis Page 575: should say "Tolsto\u\i, Lev Nikolaevich" Page 575: add page 26 under triangle inequality Page 575: Voronoi's correct name is "Vorono\u\i, Georgi\u\i\ Feodos'evich" Page 575: Walker's full name is "Walker, Alistair John" Refinements to spacing between lines, relevant to section names like <Local variables>, have also been made on pages 75, 124, 131, 137, 140, 146, 150, 156, 158, 160, 163, 168, 174, 182, 204, 225, 318, 324, 329, 334, 367, 399, 414, 433, 466, 490. ����������������������������������������������������������������������������������������������������������������������������������������������������ANSI/�����������������������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�07773517505�007752� 5����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ANSI/gb_dijk.ch�������������������������������������������������������������������������������������0000444�0001750�0001750�00000000265�07426527326�011655� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Change file for gb_dijk.w @x line 195 for (t=gg->vertices+gg->n-1; t>=gg->vertices; t--) t->backlink=NULL; @y for (t=gg->vertices+gg->n; t>gg->vertices; ) (--t)->backlink=NULL; @z �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ANSI/gb_sort.ch�������������������������������������������������������������������������������������0000444�0001750�0001750�00000002351�05532471460�011712� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Change file for gb_sort.w @x line 109 for (pp=alt_sorted+255; pp>=alt_sorted; pp--) *pp=NULL; @y for (pp=alt_sorted+256; pp>alt_sorted; ) *--pp=NULL; @z @x line 119 for (pp=gb_sorted+255; pp>=gb_sorted; pp--) *pp=NULL; @y for (pp=gb_sorted+256; pp>gb_sorted; ) *--pp=NULL; @z @x lines 121-122 for (pp=alt_sorted+255; pp>=alt_sorted; pp--) for (p=*pp; p; p=q) { @y for (pp=alt_sorted+256; pp>alt_sorted; ) for (p=*--pp; p; p=q) { @z @x line 130 for (pp=alt_sorted+255; pp>=alt_sorted; pp--) *pp=NULL; @y for (pp=alt_sorted+256; pp>alt_sorted; ) *--pp=NULL; @z @x lines 132-133 for (pp=gb_sorted+255; pp>=gb_sorted; pp--) for (p=*pp; p; p=q) { @y for (pp=gb_sorted+256; pp>gb_sorted; ) for (p=*--pp; p; p=q) { @z @x line 145 for (pp=gb_sorted+255; pp>=gb_sorted; pp--) *pp=NULL; @y for (pp=gb_sorted+256; pp>gb_sorted; ) *--pp=NULL; @z @x line 156 for (pp=alt_sorted+255; pp>=alt_sorted; pp--) *pp=NULL; @y for (pp=alt_sorted+256; pp>alt_sorted; ) *--pp=NULL; @z @x lines 158-159 for (pp=gb_sorted+255; pp>=gb_sorted; pp--) for (p=*pp; p; p=q) { @y for (pp=gb_sorted+256; pp>gb_sorted; ) for (p=*--pp; p; p=q) { @z @x line 172 for (pp=gb_sorted+255; pp>=gb_sorted; pp--) *pp=NULL; @y for (pp=gb_sorted+256; pp>gb_sorted; ) *--pp=NULL; @z ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ANSI/gb_econ.ch�������������������������������������������������������������������������������������0000444�0001750�0001750�00000000676�05532471022�011651� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Change file for gb_econ.w @x lines 386-387 for (p=node_index[ADJ_SEC]-1;p>=node_block;p--) /* bottom up */ if (p->rchild) @y for (p=node_index[ADJ_SEC];p>node_block; ) /* bottom up */ if ((--p)->rchild) @z @x lines 579-580 for (p=node_index[ADJ_SEC];p>=node_block;p--) { /* bottom up */ if (p->SIC) { /* original leaf */ @y for (p=node_index[ADJ_SEC]+1;p>node_block; ) { /* bottom up */ if ((--p)->SIC) { /* original leaf */ @z ������������������������������������������������������������������ANSI/README�����������������������������������������������������������������������������������������0000444�0001750�0001750�00000000754�05532472246�010627� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������The change files in this directory make the GraphBase sources conform better to ANSI C restrictions. Thanks to Marc van Leeuwen (maavl@cwi.nl) for contributing them. Additional corrections of this kind may be needed as more problems with the ANSI standard are noticed; if you develop any well-tested change files that improve on these, please submit them to sgb@cs.standard.edu. [To use these change files, just "mv *.ch $SGBDIR", where SGBDIR is the directory with your GraphBase sources.] ��������������������ANSI/ladders.ch�������������������������������������������������������������������������������������0000444�0001750�0001750�00000001307�05532471163�011671� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Change file for ladders.w @x lines 184-185 for (u=g->vertices+g->n-1; u>=g->vertices; u--) {@+register Arc *a; register char *p=u->name; @y for (u=g->vertices+g->n; u>g->vertices; ) {@+register Arc *a; register char *p=(--u)->name; @z @x lines 191-192 for (u=g->vertices+g->n-1; u>=g->vertices; u--) {@+register Arc *a; for (a=u->arcs; a; a=a->next) @y for (u=g->vertices+g->n; u>g->vertices; u) {@+register Arc *a; for (a=(--u)->arcs; a; a=a->next) @z @x for (uu=g->vertices+gg->n-1; uu>=g->vertices+g->n; uu--) {@+register Arc *a; for (a=uu->arcs; a; a=a->next) { @y for (uu=g->vertices+gg->n; uu>g->vertices+g->n; ) {@+register Arc *a; for (a=(--uu)->arcs; a; a=a->next) { @z �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ANSI/gb_graph.ch������������������������������������������������������������������������������������0000444�0001750�0001750�00000000400�05532470427�012017� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Change file for gb_graph.w @x lines 450-451 for (p=cur_graph->vertices+n+extra_n-1; p>=cur_graph->vertices; p--) p->name=null_string; @y for (p=cur_graph->vertices+n+extra_n; p>cur_graph->vertices; ) (--p)->name=null_string; @z ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ANSI/gb_roget.ch������������������������������������������������������������������������������������0000444�0001750�0001750�00000000452�05532471066�012045� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Change file for gb_roget.w @x lines 128-130 for (v=new_graph->vertices+n-1; v>=new_graph->vertices; v--) { j=gb_unif_rand(k); mapping[cats[j]]=v; cats[j]=cats[--k]; @y for (v=new_graph->vertices+n; v>new_graph->vertices; ) { j=gb_unif_rand(k); mapping[cats[j]]=--v; cats[j]=cats[--k]; @z ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ANSI/roget_components.ch����������������������������������������������������������������������������0000444�0001750�0001750�00000000266�05532470242�013640� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Change file for roget_components.w @x lines 244-245 for (v=g->vertices+g->n-1; v>=g->vertices; v--) { v->rank=0; @y for (v=g->vertices+g->n; v>g->vertices; ) { (--v)->rank=0; @z ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ANSI/multiply.ch������������������������������������������������������������������������������������0000444�0001750�0001750�00000000324�05532471246�012132� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Change file for multiply.w @x line 165 for (r=buffer+strlen(buffer)-1;r>=buffer;r--) { @y for (r=buffer+strlen(buffer);r>buffer; ) { @z @x line 178 if (*r=='1') *q=2*a+'1'; @y if (*--r=='1') *q=2*a+'1'; @z ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ANSI/gb_basic.ch������������������������������������������������������������������������������������0000444�0001750�0001750�00000000640�05532470732�012004� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Change file for gb_basic.w @x lines 1897-1899 for (v=g->vertices+g->n-1;v>=g->vertices;v--) {@+register Arc *a; register long mapped=0; /* has |v->map| been set? */ for (a=v->arcs;a;a=a->next) {@+register Vertex *vv=a->tip; @y for (v=g->vertices+g->n;v>g->vertices; ) {@+register Arc *a; register long mapped=0; /* has |v->map| been set? */ for (a=(--v)->arcs;a;a=a->next) {@+register Vertex *vv=a->tip; @z ������������������������������������������������������������������������������������������������ANSI/gb_save.ch�������������������������������������������������������������������������������������0000444�0001750�0001750�00000002621�05532470646�011666� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Change file for gb_save.w @x lines 641-642 for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--) { if (cur_block->cat==vrt) { @y for (cur_block=blocks+block_count;cur_block>blocks; ) { if ((--cur_block)->cat==vrt) { @z @x lines 658-659 for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--) if (cur_block->start_addr==(char *)g->vertices) { @y for (cur_block=blocks+block_count;cur_block>blocks; ) if ((--cur_block)->start_addr==(char *)g->vertices) { @z @x lines 809-814 for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--) if (cur_block->cat==vrt && cur_block->offset==0) @<Translate all |Vertex| records in |cur_block|@>; for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--) if (cur_block->cat==vrt && cur_block->offset!=0) @<Translate all |Vertex| records in |cur_block|@>; @y for (cur_block=blocks+block_count;cur_block>blocks; ) if ((--cur_block)->cat==vrt && cur_block->offset==0) @<Translate all |Vertex| records in |cur_block|@>@; for (cur_block=blocks+block_count;cur_block>blocks; ) if ((--cur_block)->cat==vrt && cur_block->offset!=0) @<Translate all |Vertex| records in |cur_block|@>@; @z @x lines 835-836 for (cur_block=blocks+block_count-1;cur_block>=blocks;cur_block--) if (cur_block->cat==ark) @y for (cur_block=blocks+block_count;cur_block>blocks; ) if ((--cur_block)->cat==ark) @z ���������������������������������������������������������������������������������������������������������������AMIGA/����������������������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�10602556206�010020� 5����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������AMIGA/README.AMIGA����������������������������������������������������������������������������������0000444�0001750�0001750�00000010025�10601511126�011500� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This file, ./AMIGA/README.AMIGA, is NOT part of the standard distribution of the Stanford GraphBase. COPYRIGHT NOTICE FOR ./AMIGA The following copyright notice extends to all files in the ./AMIGA subdirectory, but not to any part of the standard distribution of the Stanford GraphBase (which is copyright (c) 1993 by Stanford University). Copyright (c) 1994 Andreas Scherer Permission is granted to make and distribute verbatim copies of this document provided that the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this document under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. PURPOSE OF THIS FILE It describes the necessary steps to get the Stanford GraphBase (SGB) up and running on the Commodore AMIGA with the help of SAS/C++ 6.55. Also it describes the contents of the subdirectory ./AMIGA, where `.' denotes the root directory of the standard installation of SGB. FILES ADDED FOR INSTALLING SGB ON THE AMIGA The standard distribution of SGB is assumed to be installed on your AMIGA. `Installed' means that all source files from the original archive are present in a root directory (denoted by `.' in what follows) and, if appropriate, one or more subdirectories, on your machine. If you don't have SGB yet, go out and get it from ftp.cs.stanford.edu, where it resides in the directory `~ftp/pub/sgb', or possibly from some other host location. The original source files are totally left untouched (as this is expressly prohibited by the copyright notice included in every single file of SGB). For installing SGB on the AMIGA, two additional files come with this patch. They should be added to the local SGB installation in a new subdirectory. ./AMIGA/SMakefile Makefile for SAS/C++ 6.55. ./AMIGA/README.AMIGA This file. HOW TO INSTALL SGB ON THE AMIGA First read the ./README file that comes with the standard distribution of the Stanford GraphBase, but don't follow the instructions for building the targets yet. Some points are overwritten by what follows. Details of the installation process are described in ./README, though. Then install CWEB (version 3.0 or greater), which can be found in various archives; the master files reside at ftp.cs.stanford.edu; a complete AMIGA installation of CWEB (version 3.2 patch level 10) resides on the CTAN host ftp.dante.de:/pub/tex/web/c_cpp/AmigaCWEB/ and is ready to use. Copy ./AMIGA/SMakefile to the root directory of SGB and edit it as SMakefile instructs you; especially the entry for `MAKE' needs adjustment. If this is done, take a deep breath, and say "make tests". After you get the message Congratulations --- the tests have all been passed you can then say "make install". If you do this for the first time, all necessary subdirectories will be created by the `make' process. If you want to install all the demonstration programs as well as the GraphBase library, say "make installdemos" after "make install". Finally, you should say "make clean" to remove all intermediate files from the root directory. TROUBLE SHOOTING I tested the AMIGA version of SGB with the following setup: AMIGA 2000 GVP G-Force 030/50/50/8 SAS/C++ 6.56 CWEB 3.4 patch level 13 For compilation the CFLAGS macro (see ./AMIGA/SMakefile) was set to CFLAGS = DEBUG=full # OPTIMIZE INCLUDEDIR=$(SGBDIR) INCLUDEDIR=$(INCLUDEDIR) $(SYS) CPU=any # CPU=68030 MATH=standard # MATH=68882 DEFINE=_STRICT_ANSI IGNORE=85+93+100+132+154+161 NOICONS in combination with the appropriate setting of MATHLIB = LIB:scm.lib # LIB:scm881.lib Should you encounter problems with this AMIGA patch for SGB with these or different settings or should you have ideas for further improvements, like using GnuMake instead of SMake, or reducing the list of suppressed compiler warnings, contact the author of this contribution Andreas Scherer Rochusstraße 22-24 52062 Aachen Germany <scherer@physik.rwth-aachen.de> (Internet) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������AMIGA/SMakefile�������������������������������������������������������������������������������������0000644�0001750�0001750�00000021231�06141715457�011612� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file, ./AMIGA/SMakefile, is NOT part of the standard distribution # of the Stanford GraphBase. # The following copyright notice extends to this file only, not to any # part of the standard distribution of the Stanford GraphBase (which is # copyright (c) 1993 by Stanford University). # Copyright (c) 1994 Andreas Scherer # Permission is granted to make and distribute verbatim copies of this # document provided that the copyright notice and this permission notice # are preserved on all copies. # Permission is granted to copy and distribute modified versions of this # document under the conditions for verbatim copying, provided that the # entire resulting derived work is distributed under the terms of a # permission notice identical to this one. # This file is a Makefile for installing the Stanford GraphBase on the # Commodore AMIGA using SAS/C++ 6.56 with SAS/SMake 6.55. # Be sure that CWEB version 3.0 or greater is installed before proceeding! # Change SGBDIR to the directory where all GraphBase files will go: SGBDIR = Local:sgb # Change DATADIR to the directory where GraphBase data files will go: DATADIR = $(SGBDIR)/data # Change INCLUDEDIR to the directory where GraphBase header files will go: INCLUDEDIR = $(SGBDIR)/include # Change LIBDIR to the directory where GraphBase library routines will go: LIBDIR = $(SGBDIR)/lib # Change BINDIR to the directory where installdemos will put demo programs: BINDIR = $(SGBDIR)/demos # Change CWEBINPUTS to the directory where CWEB include files will go: CWEBINPUTS = $(SGBDIR)/cwebinputs # Uncomment the next line if your C uses <string.h> but not <strings.h>: SYS = DEFINE=SYSV # If you prefer optimization to debugging, change DEBUG=full to OPTIMIZE. # Adapt CPU and MATH to your system's hardware, e.g., CPU=68030 and # MATH=68882 in connection with MATHLIB=LIB:scm881.lib: CFLAGS = DEBUG=full INCLUDEDIR=$(SGBDIR) INCLUDEDIR=$(INCLUDEDIR) $(SYS) \ CPU=any MATH=standard IGNORE=85+93+100+132+154+161 NOICONS MATHLIB = LIB:scm.lib MAKE=SMake ########## You shouldn't have to change anything after this point ########## # (Well, this is NOT true, when you do this for the first time. # SMake is totally different compared to UNIX make, especially most # default productions and rules are missing, so the implicit dependencies # have to be resolved by hand. Also the compilation and linkage stages are # different compared to those of UNIX cc. Maybe it would be a good idea to # use GNU cc and GNU make?) LIBS = LIB $(LIBDIR)/libgb.lib $(MATHLIB) .SUFFIXES: .dvi .tex .w .c.o: $(CC) $(CFLAGS) $*.c .tex.dvi: tex &plain "\language=\USenglish \input " $*.tex .w.c: ctangle $*.w .w.tex: cweave $*.w .w.o: $(MAKE) $*.c $(MAKE) $*.o .w: $(MAKE) $*.c $(MAKE) $* .w.dvi: $(MAKE) $*.tex $(MAKE) $*.dvi PATCH = README.AMIGA SMakefile DATAFILES = anna.dat david.dat econ.dat games.dat homer.dat huck.dat \ jean.dat lisa.dat miles.dat roget.dat words.dat KERNELFILES = gb_flip.w gb_graph.w gb_io.w gb_sort.w KERNELOBJECTS = gb_flip.o gb_graph.o gb_io.o gb_sort.o KERNELHEADERS = gb_flip.h gb_graph.h gb_io.h gb_sort.h GENERATORFILES = gb_basic.w gb_books.w gb_econ.w gb_games.w gb_gates.w \ gb_lisa.w gb_miles.w gb_plane.w gb_raman.w gb_rand.w gb_roget.w \ gb_words.w GENERATOROBJECTS = gb_basic.o gb_books.o gb_econ.o gb_games.o gb_gates.o \ gb_lisa.o gb_miles.o gb_plane.o gb_raman.o gb_rand.o gb_roget.o \ gb_words.o GENERATORHEADERS = gb_basic.h gb_books.h gb_econ.h gb_games.h gb_gates.h \ gb_lisa.h gb_miles.h gb_plane.h gb_raman.h gb_rand.h gb_roget.h \ gb_words.h DEMOFILES = assign_lisa.w book_components.w econ_order.w football.w \ girth.w ladders.w miles_span.w multiply.w queen.w roget_components.w \ take_risc.w word_components.w DEMOPROGS = assign_lisa book_components econ_order football \ girth ladders miles_span multiply queen roget_components \ take_risc word_components MISCWEBS = boilerplate.w gb_dijk.w gb_save.w gb_types.w test_sample.w CHANGEFILES = queen_wrap.ch word_giant.ch MISCFILES = Makefile README abstract.plaintex cities.texmap blank.w \ sample.correct test.correct test.dat +The+Stanford+GraphBase+ ALL = $(DATAFILES) $(KERNELFILES) $(KERNELOBJECTS) \ $(GENERATORFILES) $(GENERATOROBJECTS) \ $(DEMOFILES) $(DEMOPROGS) \ $(MISCWEBS) $(CHANGEFILES) $(MISCFILES) OBJS = $(KERNELOBJECTS) $(GENERATOROBJECTS) gb_dijk.o gb_save.o HEADERS = $(KERNELHEADERS) $(GENERATORHEADERS) gb_dijk.h gb_save.h DEMOS = $(DEMOPROGS) help: @echo "First 'make tests';" @echo "then 'make install';" @echo "then (optionally) 'make installdemos';" @echo "then (optionally) 'make clean'." lib: libgb.lib libgb.lib: $(OBJS) #delete certified force oml libgb.lib R $(OBJS) gb_graph.o: gb_graph.c gb_flip.o: gb_flip.c gb_sort.o: gb_sort.c gb_graph.dvi: gb_graph.tex gb_flip.dvi: gb_flip.tex gb_sort.dvi: gb_sort.tex gb_basic.o: gb_basic.c gb_books.o: gb_books.c gb_econ.o: gb_econ.c gb_games.o: gb_games.c gb_gates.o: gb_gates.c gb_lisa.o: gb_lisa.c gb_miles.o: gb_miles.c gb_plane.o: gb_plane.c gb_raman.o: gb_raman.c gb_rand.o: gb_rand.c gb_roget.o: gb_roget.c gb_words.o: gb_words.c gb_basic.dvi: gb_basic.tex gb_books.dvi: gb_books.tex gb_econ.dvi: gb_econ.tex gb_games.dvi: gb_games.tex gb_gates.dvi: gb_gates.tex gb_lisa.dvi: gb_lisa.tex gb_miles.dvi: gb_miles.tex gb_plane.dvi: gb_plane.tex gb_raman.dvi: gb_raman.tex gb_rand.dvi: gb_rand.tex gb_roget.dvi: gb_roget.tex gb_words.dvi: gb_words.tex assign_lisa: assign_lisa.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) assign_lisa.o: assign_lisa.c assign_lisa.dvi: assign_lisa.tex book_components: book_components.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) book_components.o: book_components.c book_components.dvi: book_components.tex econ_order: econ_order.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) econ_order.o: econ_order.c $(CC) $(CFLAGS) DATA=far $? econ_order.dvi: econ_order.tex football: football.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) football.o: football.c football.dvi: football.tex girth: girth.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) girth.o: girth.c girth.dvi: girth.tex ladders: ladders.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) ladders.o: ladders.c ladders.dvi: ladders.tex miles_span: miles_span.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) miles_span.o: miles_span.c miles_span.dvi: miles_span.tex multiply: multiply.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) multiply.o: multiply.c multiply.dvi: multiply.tex queen: queen.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) queen.o: queen.c queen.dvi: queen.tex roget_components: roget_components.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) roget_components.o: roget_components.c roget_components.dvi: roget_components.tex take_risc: take_risc.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) take_risc.o: take_risc.c take_risc.dvi: take_risc.tex word_components: word_components.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) word_components.o: word_components.c word_components.dvi: word_components.tex gb_dijk.o: gb_dijk.c gb_save.o: gb_save.c gb_types.o: gb_types.c gb_dijk.dvi: gb_dijk.tex gb_save.dvi: gb_save.tex gb_types.dvi: gb_types.tex gb_io.o: gb_io.c $(CC) $(CFLAGS) DEFINE=DATA_DIRECTORY="$(DATADIR)/" gb_io.c gb_io.dvi: gb_io.tex test_io: gb_io.o $(CC) $(CFLAGS) LINK FROM test_io.c gb_io.o TO test_io test_graph: gb_graph.o $(CC) $(CFLAGS) LINK FROM test_graph.c gb_graph.o TO test_graph test_flip: gb_flip.o $(CC) $(CFLAGS) LINK FROM test_flip.c gb_flip.o TO test_flip test_sample: test_sample.o $(CC) $(CFLAGS) LINK FROM $? TO $@ LIB libgb.lib $(MATHLIB) test_sample.o: test_sample.c test_sample.dvi: test_sample.tex tests: test_io test_graph test_flip test_io test_graph test_flip $(MAKE) gb_sort.o $(MAKE) lib $(MAKE) test_sample - test_sample > sample.out diff test.gb test.correct diff sample.out sample.correct delete test.gb sample.out test_io test_graph test_flip test_sample @echo "Congratulations --- the tests have all been passed." # touch certified install: lib #if test ! -r certified; then echo "Please run 'make tests' first!"; fi #test -r certified $(MAKE) installdata - makedir $(LIBDIR) - copy libgb.lib $(LIBDIR) - makedir $(CWEBINPUTS) - copy boilerplate.w gb_types.w $(CWEBINPUTS) - makedir $(INCLUDEDIR) - copy $(HEADERS) Makefile $(INCLUDEDIR) installdata: $(DATAFILES) - makedir $(SGBDIR) - makedir $(DATADIR) - copy $(DATAFILES) $(DATADIR) installdemos: $(DEMOS) - makedir $(BINDIR) - copy $(DEMOPROGS) $(BINDIR) - delete $(DEMOPROGS) uninstalldemos: - delete $(BINDIR)/$(DEMOPROGS) force doc: tex &plain "\language=\USenglish \input " abstract.plaintex clean: - delete \#?.(o|c|h|tex|log|dvi|toc|idx|scn|bak|lnk) libgb.lib certified veryclean: clean - delete $(DEMOPROGS) $(PATCH) force �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/�����������������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�07773517505�010750� 5����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_rand.ch�������������������������������������������������������������������������������0000444�0001750�0001750�00000006462�07224005030�012636� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.31 extern Graph *random_graph(); extern Graph *random_bigraph(); extern long random_lengths(); @y extern Graph *random_graph(unsigned long,unsigned long,long,long,long,@| long *,long *,long,long,long); extern Graph *random_bigraph(unsigned long,unsigned long,unsigned long,@| long,long *,long *,long,long,long); extern long random_lengths(Graph *,long,long,long,long *,long); @z @x l.139 Graph *random_graph(n,m,multi,self,directed,dist_from,dist_to,min_len,max_len, seed) unsigned long n; /* number of vertices desired */ unsigned long m; /* number of arcs or edges desired */ long multi; /* allow duplicate arcs? */ long self; /* allow self loops? */ long directed; /* directed graph? */ long *dist_from; /* distribution of arc sources */ long *dist_to; /* distribution of arc destinations */ long min_len,max_len; /* bounds on random lengths */ long seed; /* random number seed */ @y Graph *random_graph(@t\1\1@> unsigned long n, /* number of vertices desired */ unsigned long m, /* number of arcs or edges desired */ long multi, /* allow duplicate arcs? */ long self, /* allow self loops? */ long directed, /* directed graph? */ long *dist_from, /* distribution of arc sources */ long *dist_to, /* distribution of arc destinations */ long min_len,long max_len, /* bounds on random lengths */ long seed@t\2\2@>) /* random number seed */ @z @x l.369 static magic_entry *walker(n,nn,dist,g) long n; /* length of |dist| vector */ long nn; /* $2^{\lceil\mskip1mu\lg n\rceil}$ */ register long *dist; /* start of distribution table, which sums to $2^{30}$ */ Graph *g; /* tables will be allocated for this graph's vertices */ @y static magic_entry *walker(@t\1\1@> long n, /* length of |dist| vector */ long nn, /* $2^{\lceil\mskip1mu\lg n\rceil}$ */ register long *dist, /* start of distribution table, which sums to $2^{30}$ */ Graph *g@t\2\2@>) /* tables will be allocated for this graph's vertices */ @z @x l.454 Graph *random_bigraph(n1,n2,m,multi,dist1,dist2,min_len,max_len,seed) unsigned long n1,n2; /* number of vertices desired in each part */ unsigned long m; /* number of edges desired */ long multi; /* allow duplicate edges? */ long *dist1, *dist2; /* distribution of edge endpoints */ long min_len,max_len; /* bounds on random lengths */ long seed; /* random number seed */ @y Graph *random_bigraph(@t\1\1@> unsigned long n1,unsigned long n2, /* number of vertices desired in each part */ unsigned long m, /* number of edges desired */ long multi, /* allow duplicate edges? */ long *dist1,long *dist2, /* distribution of edge endpoints */ long min_len,long max_len, /* bounds on random lengths */ long seed@t\2\2@>) /* random number seed */ @z @x l.523 long random_lengths(g,directed,min_len,max_len,dist,seed) Graph *g; /* graph whose lengths will be randomized */ long directed; /* is it directed? */ long min_len,max_len; /* bounds on random lengths */ long *dist; /* distribution of lengths */ long seed; /* random number seed */ @y long random_lengths(@t\1\1@> Graph *g, /* graph whose lengths will be randomized */ long directed, /* is it directed? */ long min_len,long max_len, /* bounds on random lengths */ long *dist, /* distribution of lengths */ long seed@t\2\2@>) /* random number seed */ @z ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/assign_lisa.ch���������������������������������������������������������������������������0000444�0001750�0001750�00000000460�05716652334�013551� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.72 main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ @y int main(@t\1\1@> int argc, /* the number of command-line arguments */ char *argv[]@t\2\2@>) /* an array of strings containing those arguments */ @z ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_miles.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000002570�05635275732�013045� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.14 extern Graph *miles(); @y extern Graph *miles(unsigned long,long,long,long,@| unsigned long,unsigned long,long); @z @x l.116 Graph *miles(n,north_weight,west_weight,pop_weight, max_distance,max_degree,seed) unsigned long n; /* number of vertices desired */ long north_weight; /* coefficient of latitude in the weight function */ long west_weight; /* coefficient of longitude in the weight function */ long pop_weight; /* coefficient of population in the weight function */ unsigned long max_distance; /* maximum distance in an edge, if nonzero */ unsigned long max_degree; /* maximum number of edges per vertex, if nonzero */ long seed; /* random number seed */ @y Graph *miles(@t\1\1@> unsigned long n, /* number of vertices desired */ long north_weight, /* coefficient of latitude in the weight function */ long west_weight, /* coefficient of longitude in the weight function */ long pop_weight, /* coefficient of population in the weight function */ unsigned long max_distance, /* maximum distance in an edge, if nonzero */ unsigned long max_degree, /* maximum number of edges per vertex, if nonzero */ long seed@t\2\2@>) /* random number seed */ @z @x l.394 @p long miles_distance(u,v) Vertex *u,*v; @y @p long miles_distance(Vertex *u,Vertex *v) @z @x l.401 extern long miles_distance(); @y extern long miles_distance(Vertex *,Vertex *); @z ����������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/queen.ch���������������������������������������������������������������������������������0000444�0001750�0001750�00000000044�05716657004�012367� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.26 main() @y int main(void) @z ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_dijk.ch�������������������������������������������������������������������������������0000444�0001750�0001750�00000007347�07224006063�012645� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.68 extern long dijkstra(); /* procedure to calculate shortest paths */ #define print_dijkstra_result p_dijkstra_result /* shorthand for linker */ extern void print_dijkstra_result(); /* procedure to display the answer */ @y extern long dijkstra(Vertex *,Vertex *,Graph *,long (*)(Vertex *)); /* procedure to calculate shortest paths */ #define print_dijkstra_result p_dijkstra_result /* shorthand for linker */ extern void print_dijkstra_result(Vertex *); /* procedure to display the answer */ @z @x l.143 extern void @[@] (*init_queue)(); /* create an empty priority queue for |dijkstra| */ extern void @[@] (*enqueue)(); /* insert a new element in the priority queue */ extern void @[@] (*requeue)(); /* decrease the key of an element in the queue */ extern Vertex *(*del_min)(); /* remove an element with smallest key */ @y extern void @[@] (*init_queue)(long); /* create an empty priority queue for |dijkstra| */ extern void @[@] (*enqueue)(Vertex *,long); /* insert a new element in the priority queue */ extern void @[@] (*requeue)(Vertex *,long); /* decrease the key of an element in the queue */ extern Vertex *(*del_min)(void); /* remove an element with smallest key */ @z @x l.162 static long dummy(v) Vertex *v; @y static long dummy(Vertex *v) @z @x l.169 long dijkstra(uu,vv,gg,hh) Vertex *uu; /* the starting point */ Vertex *vv; /* the ending point */ Graph *gg; /* the graph they belong to */ long @[@] (*hh)(); /* heuristic function */ @y long dijkstra(@t\1\1@> Vertex *uu, /* the starting point */ Vertex *vv, /* the ending point */ Graph *gg, /* the graph they belong to */ long (*hh)(Vertex *)@t\2\2@>) /* heuristic function */ @z @x l.257 void print_dijkstra_result(vv) Vertex *vv; /* ending vertex */ @y void print_dijkstra_result(Vertex *vv) /* ending vertex */ @z @x l.295 void @[@] (*init_queue)() = init_dlist; /* create an empty dlist */ void @[@] (*enqueue)() = enlist; /* insert a new element in dlist */ void @[@] (*requeue)() = reenlist ; /* decrease the key of an element in dlist */ Vertex *(*del_min)() = del_first; /* remove element with smallest key */ @y void @[@] (*init_queue)(long) = init_dlist; /* create an empty dlist */ void @[@] (*enqueue)(Vertex *,long) = enlist; /* insert a new element in dlist */ void @[@] (*requeue)(Vertex *,long) = reenlist ; /* decrease the key of an element in dlist */ Vertex *(*del_min)(void) = del_first; /* remove element with smallest key */ @z @x l.311 void init_dlist(d) long d; @y void init_dlist(long d) @z @x l.328 void enlist(v,d) Vertex *v; long d; @y void enlist(Vertex *v,long d) @z @x l.340 void reenlist(v,d) Vertex *v; long d; @y void reenlist(Vertex *v,long d) @z @x l.353 Vertex *del_first() @y Vertex *del_first(void) @z @x l.374 void init_128(d) long d; @y void init_128(long d) @z @x l.386 Vertex *del_128() @y Vertex *del_128(void) @z @x l.402 void enq_128(v,d) Vertex *v; /* new vertex for the queue */ long d; /* its |dist| */ @y void enq_128(@t\1\1@> Vertex *v, /* new vertex for the queue */ long d@t\2\2@>) /* its |dist| */ @z @x l.425 void req_128(v,d) Vertex *v; /* vertex to be moved to another list */ long d; /* its new |dist| */ @y void req_128(@t\1\1@> Vertex *v, /* vertex to be moved to another list */ long d@t\2\2@>) /* its new |dist| */ @z @x l.442 extern void init_dlist(); extern void enlist(); extern void reenlist(); extern Vertex *del_first(); extern void init_128(); extern Vertex *del_128(); extern void enq_128(); extern void req_128(); @y extern void init_dlist(long); extern void enlist(Vertex *,long); extern void reenlist(Vertex *,long); extern Vertex *del_first(void); extern void init_128(long); extern Vertex *del_128(void); extern void enq_128(Vertex *,long); extern void req_128(Vertex *,long); @z �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_sort.ch�������������������������������������������������������������������������������0000444�0001750�0001750�00000000336�05635275732�012721� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.75 extern void gb_linksort(); /* procedure to sort a linked list */ @y extern void gb_linksort(void *); /* procedure to sort a linked list */ @z @x l.95 void gb_linksort(l) node *l; @y void gb_linksort(node *l) @z ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_econ.ch�������������������������������������������������������������������������������0000444�0001750�0001750�00000001172�05635275732�012655� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.14 extern Graph *econ(); @y extern Graph *econ(unsigned long,unsigned long,unsigned long,long); @z @x l.190 Graph *econ(n,omit,threshold,seed) unsigned long n; /* number of vertices desired */ unsigned long omit; /* number of special vertices to omit */ unsigned long threshold; /* minimum per-64K-age in arcs leading in */ long seed; /* random number seed */ @y Graph *econ(@t\1\1@> unsigned long n, /* number of vertices desired */ unsigned long omit, /* number of special vertices to omit */ unsigned long threshold, /* minimum per-64K-age in arcs leading in */ long seed@t\2\2@>) /* random number seed */ @z ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_flip.ch�������������������������������������������������������������������������������0000444�0001750�0001750�00000001102�05716654046�012653� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.38 int main() @y int main(void) @z @x l.98 extern long gb_flip_cycle(); /* compute 55 more pseudo-random numbers */ @y extern long gb_flip_cycle(void); /* compute 55 more pseudo-random numbers */ @z @x l.126 long gb_flip_cycle() @y long gb_flip_cycle(void) @z @x l.151 void gb_init_rand(seed) long seed; @y void gb_init_rand(long seed) @z @x l.223 extern void gb_init_rand(); @y extern void gb_init_rand(long); @z @x l.244 long gb_unif_rand(m) long m; @y long gb_unif_rand(long m) @z @x l.255 extern long gb_unif_rand(); @y extern long gb_unif_rand(long); @z ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/word_components.ch�����������������������������������������������������������������������0000444�0001750�0001750�00000000044�05716657407�014501� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.19 main() @y int main(void) @z ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_books.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000004367�05635275732�013057� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.17 extern Graph *book(); extern Graph *bi_book(); @y extern Graph *book(char *,unsigned long,unsigned long,@| unsigned long,unsigned long,long,long,long); extern Graph *bi_book(char *,unsigned long,unsigned long,@| unsigned long,unsigned long,long,long,long); @z @x l.158 static Graph *bgraph(bipartite, title,n,x,first_chapter,last_chapter,in_weight,out_weight,seed) long bipartite; /* should we make the graph bipartite? */ char *title; /* identification of the selected book */ unsigned long n; /* number of vertices desired before exclusion */ unsigned long x; /* number of vertices to exclude */ unsigned long first_chapter, last_chapter; /* interval of chapters leading to edges */ long in_weight; /* weight coefficient pertaining to chapters in that interval */ long out_weight; /* weight coefficient pertaining to chapters not in that interval */ long seed; /* random number seed */ @y static Graph *bgraph(@t\1\1@> long bipartite, /* should we make the graph bipartite? */ char *title, /* identification of the selected book */ unsigned long n, /* number of vertices desired before exclusion */ unsigned long x, /* number of vertices to exclude */ unsigned long first_chapter, unsigned long last_chapter, /* interval of chapters leading to edges */ long in_weight, /* weight coefficient pertaining to chapters in that interval */ long out_weight, /* weight coefficient pertaining to chapters not in that interval */ long seed@t\2\2@>) /* random number seed */ @z @x l.185 Graph *book(title,n,x,first_chapter,last_chapter,in_weight,out_weight,seed) char *title; unsigned long n, x, first_chapter, last_chapter; long in_weight,out_weight,seed; @y Graph *book(char *title,unsigned long n,unsigned long x,@| unsigned long first_chapter,unsigned long last_chapter,@| long in_weight,long out_weight,long seed) @z @x l.191 Graph *bi_book(title,n,x,first_chapter,last_chapter,in_weight,out_weight,seed) char *title; unsigned long n, x, first_chapter, last_chapter; long in_weight,out_weight,seed; @y Graph *bi_book(char *title,unsigned long n,unsigned long x,@| unsigned long first_chapter,unsigned long last_chapter,@| long in_weight,long out_weight,long seed) @z �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/ladders.ch�������������������������������������������������������������������������������0000444�0001750�0001750�00000002154�06155371464�012675� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.90 main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ @y int main(@t\1\1@> int argc, /* the number of command-line arguments */ char *argv[]@t\2\2@>) /* an array of strings containing those arguments */ @z @x l.216 long freq_cost(v) Vertex *v; @y long freq_cost(Vertex *v) @z @x l.284 long alph_dist(p,q) register char *p, *q; @y long alph_dist(register char *p,register char *q) @z @x l.291 void plant_new_edge(v) Vertex *v; @y void plant_new_edge(Vertex *v) @z @x l.324 long hamm_dist(p,q) register char *p, *q; @y long hamm_dist(register char *p,register char *q) @z @x l.338 long alph_heur(v) Vertex *v; @y long alph_heur(Vertex *v) @z @x l.342 long hamm_heur(v) Vertex *v; @y long hamm_heur(Vertex *v) @z @x l.380 long prompt_for_five(s,p) char *s; /* string used in prompt message */ register char *p; /* where to put a string typed by the user */ @y long prompt_for_five(@t\1\1@> char *s, /* string used in prompt message */ register char *p@t\2\2@>) /* where to put a string typed by the user */ @z ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/SMakefile��������������������������������������������������������������������������������0000644�0001750�0001750�00000026672�06141715442�012534� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file, ./PROTOTYPES/SMakefile, is NOT part of the standard # distribution of the Stanford GraphBase. # The following copyright notice extends to this file only, not to any # part of the standard distribution of the Stanford GraphBase (which is # copyright (c) 1993 by Stanford University). # Copyright (c) 1994 Andreas Scherer # Permission is granted to make and distribute verbatim copies of this # document provided that the copyright notice and this permission notice # are preserved on all copies. # Permission is granted to copy and distribute modified versions of this # document under the conditions for verbatim copying, provided that the # entire resulting derived work is distributed under the terms of a # permission notice identical to this one. # This file is a Makefile for installing the Stanford GraphBase on the # Commodore AMIGA using SAS/C++ 6.56 with SAS/SMake 6.55. # Be sure that CWEB version 3.0 or greater is installed before proceeding! # Change SGBDIR to the directory where all GraphBase files will go: SGBDIR = Local:sgb # Change DATADIR to the directory where GraphBase data files will go: DATADIR = $(SGBDIR)/data # Change INCLUDEDIR to the directory where GraphBase header files will go: INCLUDEDIR = $(SGBDIR)/include # Change LIBDIR to the directory where GraphBase library routines will go: LIBDIR = $(SGBDIR)/lib # Change BINDIR to the directory where installdemos will put demo programs: BINDIR = $(SGBDIR)/demos # Change CWEBINPUTS to the directory where CWEB include files will go: CWEBINPUTS = $(SGBDIR)/cwebinputs # Uncomment the next line if your C uses <string.h> but not <strings.h>: SYS = DEFINE=SYSV # If you prefer optimization to debugging, change DEBUG=full to OPTIMIZE. # Adapt CPU and MATH to your system's hardware, e.g., CPU=68030 and # MATH=68882 in connection with MATHLIB=LIB:scm881.lib: CFLAGS = DEBUG=full INCLUDEDIR=$(SGBDIR) INCLUDEDIR=$(INCLUDEDIR) $(SYS) \ CPU=any MATH=standard DEFINE=_STRICT_ANSI IGNORE=93 NOICONS MATHLIB = LIB:scm.lib MAKE=SMake ########## You shouldn't have to change anything after this point ########## # (Well, this is NOT true, when you do this for the first time. # SMake is totally different compared to UNIX make, especially most # default productions and rules are missing, so the implicit dependencies # have to be resolved by hand. Also the compilation and linkage stages are # different compared to those of UNIX cc. Maybe it would be a good idea to # use GNU cc and GNU make?) LIBS = LIB $(LIBDIR)/libgb.lib $(MATHLIB) .SUFFIXES: .dvi .tex .w .c.o: $(CC) $(CFLAGS) $*.c .tex.dvi: tex "\language=\USenglish \input " $*.tex .w.c: ctangle $*.w $*.ch .w.tex: cweave $*.w $*.ch .w.o: $(MAKE) $*.c $(MAKE) $*.o .w: $(MAKE) $*.c $(MAKE) $* .w.dvi: $(MAKE) $*.tex $(MAKE) $*.dvi PATCH = assign_lisa.ch book_components.ch econ_order.ch football.ch \ gb_basic.ch gb_books.ch gb_dijk.ch gb_econ.ch gb_flip.ch \ gb_games.ch gb_gates.ch gb_graph.ch gb_io.ch gb_lisa.ch \ gb_miles.ch gb_plane.ch gb_raman.ch gb_rand.ch gb_roget.ch \ gb_save.ch gb_sort.ch gb_words.ch girth.ch ladders.ch \ miles_span.ch multiply.ch queen.ch README.PROTOTYPES \ roget_components.ch SMakefile take_risc.ch test_sample.ch \ word_components.ch DATAFILES = anna.dat david.dat econ.dat games.dat homer.dat huck.dat \ jean.dat lisa.dat miles.dat roget.dat words.dat KERNELFILES = gb_flip.w gb_graph.w gb_io.w gb_sort.w KERNELOBJECTS = gb_flip.o gb_graph.o gb_io.o gb_sort.o KERNELHEADERS = gb_flip.h gb_graph.h gb_io.h gb_sort.h GENERATORFILES = gb_basic.w gb_books.w gb_econ.w gb_games.w gb_gates.w \ gb_lisa.w gb_miles.w gb_plane.w gb_raman.w gb_rand.w gb_roget.w \ gb_words.w GENERATOROBJECTS = gb_basic.o gb_books.o gb_econ.o gb_games.o gb_gates.o \ gb_lisa.o gb_miles.o gb_plane.o gb_raman.o gb_rand.o gb_roget.o \ gb_words.o GENERATORHEADERS = gb_basic.h gb_books.h gb_econ.h gb_games.h gb_gates.h \ gb_lisa.h gb_miles.h gb_plane.h gb_raman.h gb_rand.h gb_roget.h \ gb_words.h DEMOFILES = assign_lisa.w book_components.w econ_order.w football.w \ girth.w ladders.w miles_span.w multiply.w queen.w roget_components.w \ take_risc.w word_components.w DEMOPROGS = assign_lisa book_components econ_order football \ girth ladders miles_span multiply queen roget_components \ take_risc word_components MISCWEBS = boilerplate.w gb_dijk.w gb_save.w gb_types.w test_sample.w CHANGEFILES = queen_wrap.ch word_giant.ch MISCFILES = Makefile README abstract.plaintex cities.texmap blank.w \ sample.correct test.correct test.dat +The+Stanford+GraphBase+ ALL = $(DATAFILES) $(KERNELFILES) $(KERNELOBJECTS) \ $(GENERATORFILES) $(GENERATOROBJECTS) \ $(DEMOFILES) $(DEMOPROGS) \ $(MISCWEBS) $(CHANGEFILES) $(MISCFILES) OBJS = $(KERNELOBJECTS) $(GENERATOROBJECTS) gb_dijk.o gb_save.o HEADERS = $(KERNELHEADERS) $(GENERATORHEADERS) gb_dijk.h gb_save.h DEMOS = $(DEMOPROGS) help: @echo "First 'make tests';" @echo "then 'make install';" @echo "then (optionally) 'make installdemos';" @echo "then (optionally) 'make clean'." lib: libgb.lib libgb.lib: $(OBJS) #delete certified force oml libgb.lib R $(OBJS) gb_graph.o: gb_graph.c gb_graph.c: gb_graph.w gb_graph.ch gb_graph.dvi: gb_graph.tex gb_graph.tex: gb_graph.w gb_graph.ch gb_flip.o: gb_flip.c gb_flip.c: gb_flip.w gb_flip.ch gb_flip.dvi: gb_flip.tex gb_flip.tex: gb_flip.w gb_flip.ch gb_sort.o: gb_sort.c gb_sort.c: gb_sort.w gb_sort.ch gb_sort.dvi: gb_sort.tex gb_sort.tex: gb_sort.w gb_sort.ch gb_basic.o: gb_basic.c gb_basic.c: gb_basic.w gb_basic.ch gb_basic.dvi: gb_basic.tex gb_basic.tex: gb_basic.w gb_basic.ch gb_books.o: gb_books.c gb_books.c: gb_books.w gb_books.ch gb_books.dvi: gb_books.tex gb_books.tex: gb_books.w gb_books.ch gb_econ.o: gb_econ.c gb_econ.c: gb_econ.w gb_econ.ch gb_econ.dvi: gb_econ.tex gb_econ.tex: gb_econ.w gb_econ.ch gb_games.o: gb_games.c gb_games.c: gb_games.w gb_games.ch gb_games.dvi: gb_games.tex gb_games.tex: gb_games.w gb_games.ch gb_gates.o: gb_gates.c gb_gates.c: gb_gates.w gb_gates.ch gb_gates.dvi: gb_gates.tex gb_gates.tex: gb_gates.w gb_gates.ch gb_lisa.o: gb_lisa.c gb_lisa.c: gb_lisa.w gb_lisa.ch gb_lisa.dvi: gb_lisa.tex gb_lisa.tex: gb_lisa.w gb_lisa.ch gb_miles.o: gb_miles.c gb_miles.c: gb_miles.w gb_miles.ch gb_miles.dvi: gb_miles.tex gb_miles.tex: gb_miles.w gb_miles.ch gb_plane.o: gb_plane.c gb_plane.c: gb_plane.w gb_plane.ch gb_plane.dvi: gb_plane.tex gb_plane.tex: gb_plane.w gb_plane.ch gb_raman.o: gb_raman.c gb_raman.c: gb_raman.w gb_raman.ch gb_raman.dvi: gb_raman.tex gb_raman.tex: gb_raman.w gb_raman.ch gb_rand.o: gb_rand.c gb_rand.c: gb_rand.w gb_rand.ch gb_rand.dvi: gb_rand.tex gb_rand.tex: gb_rand.w gb_rand.ch gb_roget.o: gb_roget.c gb_roget.c: gb_roget.w gb_roget.ch gb_roget.dvi: gb_roget.tex gb_roget.tex: gb_roget.w gb_roget.ch gb_words.o: gb_words.c gb_words.c: gb_words.w gb_words.ch gb_words.dvi: gb_words.tex gb_words.tex: gb_words.w gb_words.ch assign_lisa: assign_lisa.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) assign_lisa.o: assign_lisa.c assign_lisa.c: assign_lisa.w assign_lisa.ch assign_lisa.dvi: assign_lisa.tex assign_lisa.tex: assign_lisa.w assign_lisa.ch book_components: book_components.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) book_components.o: book_components.c book_components.c: book_components.w book_components.ch book_components.dvi: book_components.tex book_components.tex: book_components.w book_components.ch econ_order: econ_order.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) econ_order.o: econ_order.c $(CC) $(CFLAGS) DATA=far $? econ_order.c: econ_order.w econ_order.ch econ_order.dvi: econ_order.tex econ_order.tex: econ_order.w econ_order.ch football: football.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) football.o: football.c football.c: football.w football.ch football.dvi: football.tex football.tex: football.w football.ch girth: girth.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) girth.o: girth.c girth.c: girth.w girth.ch girth.dvi: girth.tex girth.tex: girth.w girth.ch ladders: ladders.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) ladders.o: ladders.c ladders.c: ladders.w ladders.ch ladders.dvi: ladders.tex ladders.tex: ladders.w ladders.ch miles_span: miles_span.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) miles_span.o: miles_span.c miles_span.c: miles_span.w miles_span.ch miles_span.dvi: miles_span.tex miles_span.tex: miles_span.w miles_span.ch multiply: multiply.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) multiply.o: multiply.c multiply.c: multiply.w multiply.ch multiply.dvi: multiply.tex multiply.tex: multiply.w multiply.ch queen: queen.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) queen.o: queen.c queen.c: queen.w queen.ch queen.dvi: queen.tex queen.tex: queen.w queen.ch roget_components: roget_components.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) roget_components.o: roget_components.c roget_components.c: roget_components.w roget_components.ch roget_components.dvi: roget_components.tex roget_components.tex: roget_components.w roget_components.ch take_risc: take_risc.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) take_risc.o: take_risc.c take_risc.c: take_risc.w take_risc.ch take_risc.dvi: take_risc.tex take_risc.tex: take_risc.w take_risc.ch word_components: word_components.o $(CC) $(CFLAGS) LINK FROM $? TO $@ $(LIBS) word_components.o: word_components.c word_components.c: word_components.w word_components.ch word_components.dvi: word_components.tex word_components.tex: word_components.w word_components.ch gb_dijk.o: gb_dijk.c gb_dijk.c: gb_dijk.w gb_dijk.ch gb_dijk.dvi: gb_dijk.tex gb_dijk.tex: gb_dijk.w gb_dijk.ch gb_save.o: gb_save.c gb_save.c: gb_save.w gb_save.ch gb_save.dvi: gb_save.tex gb_save.tex: gb_save.w gb_save.ch gb_types.o: gb_types.c gb_types.c: gb_types.w ctangle gb_types.w gb_types.dvi: gb_types.tex gb_types.tex: gb_types.w cweave gb_types.w gb_io.o: gb_io.c $(CC) $(CFLAGS) DEFINE=DATA_DIRECTORY="$(DATADIR)/" gb_io.c gb_io.c: gb_io.w gb_io.ch gb_io.dvi: gb_io.tex gb_io.tex: gb_io.w gb_io.ch test_io: gb_io.o $(CC) $(CFLAGS) LINK FROM test_io.c gb_io.o TO test_io test_graph: gb_graph.o $(CC) $(CFLAGS) LINK FROM test_graph.c gb_graph.o TO test_graph test_flip: gb_flip.o $(CC) $(CFLAGS) LINK FROM test_flip.c gb_flip.o TO test_flip test_sample: test_sample.o $(CC) $(CFLAGS) LINK FROM $? TO $@ LIB libgb.lib $(MATHLIB) test_sample.o: test_sample.c test_sample.c: test_sample.w test_sample.ch test_sample.dvi: test_sample.tex test_sample.tex: test_sample.w test_sample.ch tests: test_io test_graph test_flip test_io test_graph test_flip $(MAKE) gb_sort.o $(MAKE) lib $(MAKE) test_sample - test_sample > sample.out diff test.gb test.correct diff sample.out sample.correct delete test.gb sample.out test_io test_graph test_flip test_sample @echo "Congratulations --- the tests have all been passed." # touch certified install: lib #if test ! -r certified; then echo "Please run 'make tests' first!"; fi #test -r certified $(MAKE) installdata - makedir $(LIBDIR) - copy libgb.lib $(LIBDIR) - makedir $(CWEBINPUTS) - copy boilerplate.w gb_types.w $(CWEBINPUTS) - makedir $(INCLUDEDIR) - copy $(HEADERS) Makefile $(INCLUDEDIR) installdata: $(DATAFILES) - makedir $(SGBDIR) - makedir $(DATADIR) - copy $(DATAFILES) $(DATADIR) installdemos: $(DEMOS) - makedir $(BINDIR) - copy $(DEMOPROGS) $(BINDIR) - delete $(DEMOPROGS) uninstalldemos: - delete $(BINDIR)/$(DEMOPROGS) force doc: tex "\language=\USenglish \input " abstract.plaintex clean: - delete \#?.(o|c|h|tex|log|dvi|toc|idx|scn|bak|lnk) libgb.lib certified veryclean: clean - delete $(DEMOPROGS) force - delete $(PATCH) force ����������������������������������������������������������������������PROTOTYPES/gb_games.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000002562�05635275732�013031� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.14 extern Graph *games(); @y extern Graph *games(unsigned long,long,long,long,long,long,long,long); @z @x l.174 Graph *games(n,ap0_weight,upi0_weight,ap1_weight,upi1_weight, first_day,last_day,seed) unsigned long n; /* number of vertices desired */ long ap0_weight; /* coefficient of |ap0| in the weight function */ long ap1_weight; /* coefficient of |ap1| in the weight function */ long upi0_weight; /* coefficient of |upi0| in the weight function */ long upi1_weight; /* coefficient of |upi1| in the weight function */ long first_day; /* lower cutoff for games to be considered */ long last_day; /* upper cutoff for games to be considered */ long seed; /* random number seed */ @y Graph *games(@t\1\1@> unsigned long n, /* number of vertices desired */ long ap0_weight, /* coefficient of |ap0| in the weight function */ long upi0_weight, /* coefficient of |ap1| in the weight function */ long ap1_weight, /* coefficient of |upi0| in the weight function */ long upi1_weight, /* coefficient of |upi1| in the weight function */ long first_day, /* lower cutoff for games to be considered */ long last_day, /* upper cutoff for games to be considered */ long seed@t\2\2@>) /* random number seed */ @z @x l.440 static Vertex *team_lookup() /* read and decode an abbreviation */ @y static Vertex *team_lookup(void) /* read and decode an abbreviation */ @z ����������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_graph.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000013457�06155371427�013037� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.28 int main() @y int main(void) @z @x l.266 char *gb_alloc(n,s) long n; /* number of consecutive bytes desired */ Area s; /* storage area that will contain the new block */ @y char *gb_alloc(@t\1\1@> long n, /* number of consecutive bytes desired */ Area s@t\2\2@>) /* storage area that will contain the new block */ @z @x l.297 void gb_free(s) Area s; @y void gb_free(Area s) @z @x l.311 extern char *gb_alloc(); /* allocate another block for an area */ #define gb_typed_alloc(n,t,s) @[@t\quad@>\ @[(t*)@]gb_alloc((long)((n)*@[sizeof@](t)),s)@] extern void gb_free(); /* deallocate all blocks for an area */ @y extern char *gb_alloc(long,Area); /* allocate another block for an area */ #define gb_typed_alloc(n,t,s) @[@t\quad@>\ @[(t*)@]gb_alloc((long)((n)*@[sizeof@](t)),s)@] extern void gb_free(Area); /* deallocate all blocks for an area */ @z @x l.442 Graph *gb_new_graph(n) long n; /* desired number of vertices */ @y Graph *gb_new_graph(long n) /* desired number of vertices */ @z @x l.476 extern void make_compound_id(); /* routine to set one |id| field from another */ extern void make_double_compound_id(); /* ditto, but from two others */ @y extern void make_compound_id(Graph *,char *,Graph *,char *); /* routine to set one |id| field from another */ extern void make_double_compound_id(Graph *,char *,Graph *,char *,@| Graph *,char *); /* ditto, but from two others */ @z @x l.485 void make_compound_id(g,s1,gg,s2) /* |sprintf(g->id,"%s%s%s",s1,gg->id,s2)| */ Graph *g; /* graph whose |id| is to be set */ char *s1; /* string for the beginning of the new |id| */ Graph *gg; /* graph whose |id| is to be copied */ char *s2; /* string for the end of the new |id| */ @y void make_compound_id(@t\1\1@> /* |sprintf(g->id,"%s%s%s",s1,gg->id,s2)| */ Graph *g, /* graph whose |id| is to be set */ char *s1, /* string for the beginning of the new |id| */ Graph *gg, /* graph whose |id| is to be copied */ char *s2@t\2\2@>) /* string for the end of the new |id| */ @z @x l.498 void make_double_compound_id(g,s1,gg,s2,ggg,s3) /* |sprintf(g->id,"%s%s%s%s%s",s1,gg->id,s2,ggg->id,s3)| */ Graph *g; /* graph whose |id| is to be set */ char *s1; /* string for the beginning of the new |id| */ Graph *gg; /* first graph whose |id| is to be copied */ char *s2; /* string for the middle of the new |id| */ Graph *ggg; /* second graph whose |id| is to be copied */ char *s3; /* string for the end of the new |id| */ @y void make_double_compound_id(@t\1\1@> /* |sprintf(g->id,"%s%s%s%s%s",s1,gg->id,s2,ggg->id,s3)| */ Graph *g, /* graph whose |id| is to be set */ char *s1, /* string for the beginning of the new |id| */ Graph *gg, /* first graph whose |id| is to be copied */ char *s2, /* string for the middle of the new |id| */ Graph *ggg, /* second graph whose |id| is to be copied */ char *s3@t\2\2@>) /* string for the end of the new |id| */ @z @x l.549 Arc *gb_virgin_arc() @y Arc *gb_virgin_arc(void) @z @x l.581 void gb_new_arc(u,v,len) Vertex *u, *v; /* a newly created arc will go from |u| to |v| */ long len; /* its length */ @y void gb_new_arc(@t\1\1@> Vertex *u,Vertex *v, /* a newly created arc will go from |u| to |v| */ long len@t\2\2@>) /* its length */ @z @x l.626 void gb_new_edge(u,v,len) Vertex *u, *v; /* new arcs will go from |u| to |v| and from |v| to |u| */ long len; /* their length */ @y void gb_new_edge(@t\1\1@> Vertex *u,Vertex *v, /* new arcs will go from |u| to |v| and from |v| to |u| */ long len@t\2\2@>) /* their length */ @z @x l.689 char *gb_save_string(s) register char *s; /* the string to be copied */ @y char *gb_save_string(register char *s) /* the string to be copied */ @z @x l.772 void switch_to_graph(g) Graph *g; @y void switch_to_graph(Graph *g) @z @x l.790 void gb_recycle(g) Graph *g; @y void gb_recycle(Graph *g) @z @x l.804 extern Graph*gb_new_graph(); /* create a new graph structure */ extern void gb_new_arc(); /* append an arc to the current graph */ extern Arc*gb_virgin_arc(); /* allocate a new |Arc| record */ extern void gb_new_edge(); /* append an edge (two arcs) to the current graph */ extern char*gb_save_string(); /* store a string in the current graph */ extern void switch_to_graph(); /* save allocation variables, swap in others */ extern void gb_recycle(); /* delete a graph structure */ @y extern Graph*gb_new_graph(long); /* create a new graph structure */ extern void gb_new_arc(Vertex *,Vertex *,long); /* append an arc to the current graph */ extern Arc*gb_virgin_arc(void); /* allocate a new |Arc| record */ extern void gb_new_edge(Vertex *,Vertex *,long); /* append an edge (two arcs) to the current graph */ extern char*gb_save_string(register char *); /* store a string in the current graph */ extern void switch_to_graph(Graph *); /* save allocation variables, swap in others */ extern void gb_recycle(Graph *); /* delete a graph structure */ @z @x l.839 extern void hash_in(); /* input a name to the hash table of current graph */ extern Vertex* hash_out(); /* find a name in hash table of current graph */ extern void hash_setup(); /* create a hash table for a given graph */ extern Vertex* hash_lookup(); /* find a name in a given graph */ @y extern void hash_in(Vertex *); /* input a name to the hash table of current graph */ extern Vertex* hash_out(char *); /* find a name in hash table of current graph */ extern void hash_setup(Graph *); /* create a hash table for a given graph */ extern Vertex* hash_lookup(char *,Graph *); /* find a name in a given graph */ @z @x l.855 void hash_in(v) Vertex *v; @y void hash_in(Vertex *v) @z @x l.898 Vertex* hash_out(s) char* s; @y Vertex* hash_out(char *s) @z @x l.909 void hash_setup(g) Graph *g; @y void hash_setup(Graph *g) @z @x l.924 Vertex* hash_lookup(s,g) char *s; Graph *g; @y Vertex* hash_lookup(char *s,Graph *g) @z �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_roget.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000001330�05635275732�013045� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.14 extern Graph *roget(); @y extern Graph *roget(unsigned long,unsigned long,unsigned long,long); @z @x l.78 Graph *roget(n,min_distance,prob,seed) unsigned long n; /* number of vertices desired */ unsigned long min_distance; /* smallest inter-category distance allowed in an arc */ unsigned long prob; /* 65536 times the probability of rejecting an arc */ long seed; /* random number seed */ @y Graph *roget(@t\1\1@> unsigned long n, /* number of vertices desired */ unsigned long min_distance, /* smallest inter-category distance allowed in an arc */ unsigned long prob, /* 65536 times the probability of rejecting an arc */ long seed@t\2\2@>) /* random number seed */ @z ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/roget_components.ch����������������������������������������������������������������������0000444�0001750�0001750�00000000460�05716657142�014644� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.45 main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ @y int main(@t\1\1@> int argc, /* the number of command-line arguments */ char *argv[]@t\2\2@>) /* an array of strings containing those arguments */ @z ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/miles_span.ch����������������������������������������������������������������������������0000444�0001750�0001750�00000010456�05635275732�013420� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.99 main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ @y int main(@t\1\1@> int argc, /* the number of command-line arguments */ char *argv[]@t\2\2@>) /* an array of strings containing those arguments */ @z @x l.197 report(u,v,l) Vertex *u,*v; /* adjacent vertices in the minimum spanning tree */ long l; /* the length of the edge between them */ @y void report(@t\1\1@> Vertex *u,Vertex *v, /* adjacent vertices in the minimum spanning tree */ long l@t\2\2@>) /* the length of the edge between them */ @z @x l.378 unsigned long krusk(g) Graph *g; @y unsigned long krusk(Graph *g) @z @x l.498 void @[@] (*init_queue)(); /* create an empty priority queue */ void @[@] (*enqueue)(); /* insert a new element in the priority queue */ void @[@] (*requeue)(); /* decrease the key of an element in the queue */ Vertex *(*del_min)(); /* remove an element with smallest key */ @y void @[@] (*init_queue)(long); /* create an empty priority queue */ void @[@] (*enqueue)(Vertex *,long); /* insert a new element in the priority queue */ void @[@] (*requeue)(Vertex *,long); /* decrease the key of an element in the queue */ Vertex *(*del_min)(void); /* remove an element with smallest key */ @z @x l.513 unsigned long jar_pr(g) Graph *g; @y unsigned long jar_pr(Graph *g) @z @x l.610 void init_heap(d) /* makes the heap empty */ long d; @y void init_heap(long d) /* makes the heap empty */ @z @x l.624 void enq_heap(v,d) Vertex *v; /* vertex that is entering the queue */ long d; /* its key (aka |dist|) */ @y void enq_heap(@t\1\1@> Vertex *v, /* vertex that is entering the queue */ long d@t\2\2@>) /* its key (aka |dist|) */ @z @x l.651 void req_heap(v,d) Vertex *v; /* vertex whose key is being reduced */ long d; /* its new |dist| */ @y void req_heap(@t\1\1@> Vertex *v, /* vertex whose key is being reduced */ long d@t\2\2@>) /* its new |dist| */ @z @x l.682 Vertex *del_heap() @y Vertex *del_heap(void) @z @x l.797 void init_F_heap(d) long d; @y void init_F_heap(long d) @z @x l.860 void enq_F_heap(v,d) Vertex *v; /* vertex that is entering the queue */ long d; /* its key (aka |dist|) */ @y void enq_F_heap(@t\1\1@> Vertex *v, /* vertex that is entering the queue */ long d@t\2\2@>) /* its key (aka |dist|) */ @z @x l.901 void req_F_heap(v,d) Vertex *v; /* vertex whose key is being reduced */ long d; /* its new |dist| */ @y void req_F_heap(@t\1\1@> Vertex *v, /* vertex whose key is being reduced */ long d@t\2\2@>) /* its new |dist| */ @z @x l.970 Vertex *del_F_heap() @y Vertex *del_F_heap(void) @z @x l.1155 qunite(m,q,mm,qq,h) register long m,mm; /* number of nodes in the forests */ register Arc *q,*qq; /* binomial trees in the forests, linked by |qsib| */ Arc *h; /* |h->qsib| will get the result */ @y void qunite(@t\1\1@> register long m, /* number of nodes in the forests */ register Arc *q, /* binomial trees in the forests, linked by |qsib| */ register long mm, /* number of nodes in the forests */ register Arc *qq, /* binomial trees in the forests, linked by |qsib| */ Arc *h@t\2\2@>) /* |h->qsib| will get the result */ @z @x l.1257 qenque(h,a) Arc *h; /* header of a binomial queue */ Arc *a; /* new element for that queue */ @y void qenque(@t\1\1@> Arc *h, /* header of a binomial queue */ Arc *a@t\2\2@>) /* new element for that queue */ @z @x l.1272 qmerge(h,hh) Arc *h; /* header of binomial queue that will receive the result */ Arc *hh; /* header of binomial queue that will be absorbed */ @y void qmerge(@t\1\1@> Arc *h, /* header of binomial queue that will receive the result */ Arc *hh@t\2\2@>) /* header of binomial queue that will be absorbed */ @z @x l.1291 Arc *qdel_min(h) Arc *h; /* header of binomial queue */ @y Arc *qdel_min(Arc *h) /* header of binomial queue */ @z @x l.1339 qtraverse(h,visit) Arc *h; /* head of binomial queue to be unraveled */ void @[@] (*visit)(); /* procedure to be invoked on each node */ @y void qtraverse(@t\1\1@> Arc *h, /* head of binomial queue to be unraveled */ void (*visit)(register Arc *)@t\2\2@>) /* procedure to be invoked on each node */ @z @x l.1392 unsigned long cher_tar_kar(g) Graph *g; @y unsigned long cher_tar_kar(Graph *g) @z @x l.1614 void note_edge(a) Arc *a; @y void note_edge(Arc *a) @z ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_gates.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000011773�05635275732�013044� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.27 extern Graph *risc(); /* make a network for a microprocessor */ extern Graph *prod(); /* make a network for high-speed multiplication */ extern void print_gates(); /* write a network to standard output file */ extern long gate_eval(); /* evaluate a network */ extern Graph *partial_gates(); /* reduce network size */ extern long run_risc(); /* simulate the microprocessor */ @y extern Graph *risc(unsigned long); /* make a network for a microprocessor */ extern Graph *prod(unsigned long,unsigned long); /* make a network for high-speed multiplication */ extern void print_gates(Graph *); /* write a network to standard output file */ extern long gate_eval(Graph *,char *,char *); /* evaluate a network */ extern Graph *partial_gates(Graph *,unsigned long,unsigned long,long,char *); /* reduce network size */ extern long run_risc(Graph *,unsigned long [],unsigned long,unsigned long); /* simulate the microprocessor */ @z @x l.130 long gate_eval(g,in_vec,out_vec) Graph *g; /* graph with gates as vertices */ char *in_vec; /* string for input values, or |NULL| */ char *out_vec; /* string for output values, or |NULL| */ @y long gate_eval(@t\1\1@> Graph *g, /* graph with gates as vertices */ char *in_vec, /* string for input values, or |NULL| */ char *out_vec@t\2\2@>) /* string for output values, or |NULL| */ @z @x l.215 Graph *risc(regs) unsigned long regs; /* number of registers supported */ @y Graph *risc(unsigned long regs) /* number of registers supported */ @z @x l.412 static Vertex* new_vert(t) char t; /* the type of the new gate */ @y static Vertex* new_vert(char t) /* the type of the new gate */ @z @x l.445 static Vertex* make2(t,v1,v2) char t; /* the type of the new gate */ Vertex *v1,*v2; @y static Vertex* make2(@t\1\1@> char t, /* the type of the new gate */ Vertex *v1,Vertex *v2@t\2\2@>) @z @x l.454 static Vertex* make3(t,v1,v2,v3) char t; /* the type of the new gate */ Vertex *v1,*v2,*v3; @y static Vertex* make3(@t\1\1@> char t, /* the type of the new gate */ Vertex *v1,Vertex *v2,Vertex *v3@t\2\2@>) @z @x l.464 static Vertex* make4(t,v1,v2,v3,v4) char t; /* the type of the new gate */ Vertex *v1,*v2,*v3,*v4; @y static Vertex* make4(@t\1\1@> char t, /* the type of the new gate */ Vertex *v1,Vertex *v2,Vertex *v3,Vertex *v4@t\2\2@>) @z @x l.475 static Vertex* make5(t,v1,v2,v3,v4,v5) char t; /* the type of the new gate */ Vertex *v1,*v2,*v3,*v4,*v5; @y static Vertex* make5(@t\1\1@> char t, /* the type of the new gate */ Vertex *v1,Vertex *v2,Vertex *v3,Vertex *v4,Vertex *v5@t\2\2@>) @z @x l.496 static Vertex* comp(v) Vertex *v; @y static Vertex* comp(Vertex *v) @z @x l.514 static Vertex* make_xor(u,v) Vertex *u,*v; @y static Vertex* make_xor(Vertex *u,Vertex *v) @z @x l.876 static void make_adder(n,x,y,z,carry,add) unsigned long n; /* number of bits */ Vertex *x[],*y[]; /* input gates */ Vertex *z[]; /* output gates */ Vertex *carry; /* add this to |y|, unless it's null */ char add; /* should we add or subtract? */ @y static void make_adder(@t\1\1@> unsigned long n, /* number of bits */ Vertex *x[],Vertex *y[], /* input gates */ Vertex *z[], /* output gates */ Vertex *carry, /* add this to |y|, unless it's null */ char add@t\2\2@>) /* should we add or subtract? */ @z @x l.992 long run_risc(g,rom,size,trace_regs) Graph *g; /* graph output by |risc| */ unsigned long rom[]; /* contents of read-only memory */ unsigned long size; /* length of |rom| vector */ unsigned long trace_regs; /* if nonzero, this many registers will be traced */ @y long run_risc(@t\1\1@> Graph *g, /* graph output by |risc| */ unsigned long rom[], /* contents of read-only memory */ unsigned long size, /* length of |rom| vector */ unsigned long trace_regs@t\2\2@>) /* if nonzero, this many registers will be traced */ @z @x l.1097 static void pr_gate(v) Vertex *v; @y static void pr_gate(Vertex *v) @z @x l.1117 void print_gates(g) Graph *g; @y void print_gates(Graph *g) @z @x l.1146 static Graph* reduce(g) Graph *g; @y static Graph* reduce(Graph *g) @z @x l.1487 Graph* prod(m,n) unsigned long m,n; /* lengths of the binary numbers to be multiplied */ @y Graph* prod(unsigned long m,unsigned long n) /* lengths of the binary numbers to be multiplied */ @z @x l.1897 Graph *partial_gates(g,r,prob,seed,buf) Graph *g; /* generalized gate graph */ unsigned long r; /* the number of initial gates to leave untouched */ unsigned long prob; /* scaled probability of not touching subsequent input gates */ long seed; /* seed value for random number generation */ char *buf; /* optional parameter for information about partial assignment */ @y Graph *partial_gates(@t\1\1@> Graph *g, /* generalized gate graph */ unsigned long r, /* the number of initial gates to leave untouched */ unsigned long prob, /* scaled probability of not touching subsequent input gates */ long seed, /* seed value for random number generation */ char *buf@t\2\2@>) /* optional parameter for information about partial assignment */ @z �����PROTOTYPES/football.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000001365�05635275732�013067� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.61 main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ @y int main(@t\1\1@> int argc, /* the number of command-line arguments */ char *argv[]@t\2\2@>) /* an array of strings containing those arguments */ @z @x l.140 Vertex *prompt_for_team(s) char *s; /* string used in prompt message */ @y Vertex *prompt_for_team(char *s) /* string used in prompt message */ @z @x l.207 node *new_node(x,d) node *x; /* an old node that the new node will call |prev| */ long d; /* incremental change to |tot_len| */ @y node *new_node(@t\1\1@> node *x, /* an old node that the new node will call |prev| */ long d@t\2\2@>) /* incremental change to |tot_len| */ @z ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/multiply.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000001302�05635275732�013133� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.38 main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ @y int main(@t\1\1@> int argc, /* the number of command-line arguments */ char *argv[]@t\2\2@>) /* an array of strings containing those arguments */ @z @x l.200 decimal_to_binary(x,s,n) char *x; /* decimal string */ char *s; /* binary string */ long n; /* length of |s| */ @y void decimal_to_binary(@t\1\1@> char *x, /* decimal string */ char *s, /* binary string */ long n@t\2\2@>) /* length of |s| */ @z @x l.282 long depth(g) Graph *g; /* graph with gates as vertices */ @y long depth(Graph *g) /* graph with gates as vertices */ @z ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/econ_order.ch����������������������������������������������������������������������������0000444�0001750�0001750�00000000460�05716652655�013402� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.80 main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ @y int main(@t\1\1@> int argc, /* the number of command-line arguments */ char *argv[]@t\2\2@>) /* an array of strings containing those arguments */ @z ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/girth.ch���������������������������������������������������������������������������������0000444�0001750�0001750�00000000044�05716656215�012372� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.65 main() @y int main(void) @z ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_basic.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000021225�05716653462�013013� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.16 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 */ @y extern Graph *board(long,long,long,long,long,long,long); /* moves on generalized chessboards */ extern Graph *simplex(unsigned long,long,long,long,long,long,long); /* generalized triangular configurations */ extern Graph *subsets(unsigned long,long,long,long,long,long,unsigned long,long); /* patterns of subset intersection */ extern Graph *perms(long,long,long,long,long,unsigned long,long); /* permutations of a multiset */ extern Graph *parts(unsigned long,unsigned long,unsigned long,long); /* partitions of an integer */ extern Graph *binary(unsigned long,unsigned long,long); /* binary trees */ @# extern Graph *complement(Graph *,long,long,long); /* the complement of a graph */ extern Graph *gunion(Graph *,Graph *,long,long); /* the union of two graphs */ extern Graph *intersection(Graph *,Graph *,long,long); /* the intersection of two graphs */ extern Graph *lines(Graph *,long); /* the line graph of a graph */ extern Graph *product(Graph *,Graph *,long,long); /* the product of two graphs */ extern Graph *induced(Graph *,char *,long,long,long); /* a graph induced from another */ @z @x l.176 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? */ @y Graph *board(@t\1\1@> long n1,long n2,long n3,long n4, /* size of board desired */ long piece, /* type of moves desired */ long wrap, /* mask for coordinate positions that wrap around */ long directed@t\2\2@>) /* should the graph be directed? */ @z @x l.493 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? */ @y Graph *simplex(@t\1\1@> unsigned long n, /* the constant sum of all coordinates */ long n0,long n1,long n2,long n3,long n4, /* constraints on coordinates */ long directed@t\2\2@>) /* should the graph be directed? */ @z @x l.732 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? */ @y Graph *subsets(@t\1\1@> unsigned long n, /* the number of elements in the multiset */ long n0,long n1,long n2,long n3,long n4, /* multiplicities of elements */ unsigned long size_bits, /* intersection sizes that trigger arcs */ long directed@t\2\2@>) /* should the graph be directed? */ @z @x l.886 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? */ @y Graph *perms(@t\1\1@> long n0,long n1,long n2,long n3,long n4, /* composition of the multiset */ unsigned long max_inv, /* maximum number of inversions */ long directed@t\2\2@>) /* should the graph be directed? */ @z @x l.1037 static char *short_imap="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz_^~&@@,;.:?!%#$+-*/|<=>()[]{}`'"; @y static char *short_imap= "0123456789"@| "ABCDEFGHIJKLMNOPQRSTUVWXYZ"@| "abcdefghijklmnopqrstuvwxyz"@| "_^~&@@,;.:?!%#$+-*/|<=>()[]{}`'"; @z @x l.1098 Graph *parts(n,max_parts,max_size,directed) unsigned long n; /* the number being partitioned */ unsigned long max_parts; /* maximum number of parts */ unsigned long max_size; /* maximum size of each part */ long directed; /* should the graph be directed? */ @y Graph *parts(@t\1\1@> unsigned long n, /* the number being partitioned */ unsigned long max_parts, /* maximum number of parts */ unsigned long max_size, /* maximum size of each part */ long directed@t\2\2@>) /* should the graph be directed? */ @z @x l.1290 Graph *binary(n,max_height,directed) unsigned long n; /* the number of internal nodes */ unsigned long max_height; /* maximum height of a leaf */ long directed; /* should the graph be directed? */ @y Graph *binary(@t\1\1@> unsigned long n, /* the number of internal nodes */ unsigned long max_height, /* maximum height of a leaf */ long directed@t\2\2@>) /* should the graph be directed? */ @z @x l.1545 Graph *complement(g,copy,self,directed) Graph *g; /* graph to be complemented */ long copy; /* should we double-complement? */ long self; /* should we produce self-loops? */ long directed; /* should the graph be directed? */ @y Graph *complement(@t\1\1@> Graph *g, /* graph to be complemented */ long copy, /* should we double-complement? */ long self, /* should we produce self-loops? */ long directed@t\2\2@>) /* should the graph be directed? */ @z @x l.1642 Graph *gunion(g,gg,multi,directed) Graph *g,*gg; /* graphs to be united */ long multi; /* should we reproduce multiple arcs? */ long directed; /* should the graph be directed? */ @y Graph *gunion(@t\1\1@> Graph *g,Graph *gg, /* graphs to be united */ long multi, /* should we reproduce multiple arcs? */ long directed@t\2\2@>) /* should the graph be directed? */ @z @x l.1723 Graph *intersection(g,gg,multi,directed) Graph *g,*gg; /* graphs to be intersected */ long multi; /* should we reproduce multiple arcs? */ long directed; /* should the graph be directed? */ @y Graph *intersection(@t\1\1@> Graph *g,Graph *gg, /* graphs to be intersected */ long multi, /* should we reproduce multiple arcs? */ long directed@t\2\2@>) /* should the graph be directed? */ @z @x l.1836 Graph *lines(g,directed) Graph *g; /* graph whose lines will become vertices */ long directed; /* should the graph be directed? */ @y Graph *lines(@t\1\1@> Graph *g, /* graph whose lines will become vertices */ long directed@t\2\2@>) /* should the graph be directed? */ @z @x l.2010 Graph *product(g,gg,type,directed) Graph *g,*gg; /* graphs to be multiplied */ long type; /* |cartesian|, |direct|, or |strong| */ long directed; /* should the graph be directed? */ @y Graph *product(@t\1\1@> Graph *g,Graph *gg, /* graphs to be multiplied */ long type, /* |cartesian|, |direct|, or |strong| */ long directed@t\2\2@>) /* should the graph be directed? */ @z @x l.2170 Graph *bi_complete(n1,n2,directed) unsigned long n1; /* size of first part */ unsigned long n2; /* size of second part */ long directed; /* should all arcs go from first part to second? */ @y Graph *bi_complete(@t\1\1@> unsigned long n1, /* size of first part */ unsigned long n2, /* size of second part */ long directed@t\2\2@>) /* should all arcs go from first part to second? */ @z @x l.2223 Graph *wheel(n,n1,directed) unsigned long n; /* size of the rim */ unsigned long n1; /* number of center points */ long directed; /* should all arcs go from center to rim and around? */ @y Graph *wheel(@t\1\1@> unsigned long n, /* size of the rim */ unsigned long n1, /* number of center points */ long directed@t\2\2@>) /* should all arcs go from center to rim and around? */ @z @x l.2244 extern Graph *bi_complete(); extern Graph *wheel(); /* standard applications of |induced| */ @y extern Graph *bi_complete(unsigned long,unsigned long,long); extern Graph *wheel(unsigned long,unsigned long,long); /* standard applications of |induced| */ @z @x l.2248 Graph *induced(g,description,self,multi,directed) Graph *g; /* graph marked for induction in its |ind| fields */ char *description; /* string to be mentioned in |new_graph->id| */ long self; /* should self-loops be permitted? */ long multi; /* should multiple arcs be permitted? */ long directed; /* should the graph be directed? */ @y Graph *induced(@t\1\1@> Graph *g, /* graph marked for induction in its |ind| fields */ char *description, /* string to be mentioned in |new_graph->id| */ long self, /* should self-loops be permitted? */ long multi, /* should multiple arcs be permitted? */ long directed@t\2\2@>) /* should the graph be directed? */ @z ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_raman.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000002370�05635275732�013030� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.32 extern Graph *raman(); @y extern Graph *raman(long,long,unsigned long,unsigned long); @z @x l.92 Graph *raman(p,q,type,reduce) long p; /* one less than the desired degree; must be prime */ long q; /* size parameter; must be prime and properly related to |type| */ unsigned long type; /* selector between different possible constructions */ unsigned long reduce; /* if nonzero, multiple edges and self-loops won't occur */ @y Graph *raman(@t\1\1@> long p, /* one less than the desired degree; must be prime */ long q, /* size parameter; must be prime and properly related to |type| */ unsigned long type, /* selector between different possible constructions */ unsigned long reduce@t\2\2@>) /* if nonzero, multiple edges and self-loops won't occur */ @z @x l.481 static void deposit(a,b,c,d) long a,b,c,d; /* a solution to $a^2+b^2+c^2+d^2=p$ */ @y static void deposit(long a,long b,long c,long d) /* a solution to $a^2+b^2+c^2+d^2=p$ */ @z @x l.697 static long lin_frac(a,k) long a; /* the number being transformed; $q$ represents $\infty$ */ long k; /* index into |gen| table */ @y static long lin_frac(@t\1\1@> long a, /* the number being transformed; $q$ represents $\infty$ */ long k@t\2\2@>) /* index into |gen| table */ @z ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_io.ch���������������������������������������������������������������������������������0000444�0001750�0001750�00000007645�06155371514�012344� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.35 int main() @y int main(void) @z @x l.123 static void fill_buf() @y static void fill_buf(void) @z @x l.185 static char *imap="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz_^~&@@,;.:?!%#$+-*/|\\<=>()[]{}`'\" \n"; @y static char *imap= "0123456789"@| "ABCDEFGHIJKLMNOPQRSTUVWXYZ"@| "abcdefghijklmnopqrstuvwxyz"@| "_^~&@@,;.:?!%#$+-*/|\\<=>()[]{}`'\" \n"; @z @x l.191 char imap_chr(d) long d; { return d<0 || d>strlen(imap)? '\0': imap[d]; } @y char imap_chr(long d) { return((char)(d<0 || d>strlen(imap)? '\0': imap[d])); } @z @x l.197 long imap_ord(c) char c; @y long imap_ord(char c) @z @x l.206 extern char imap_chr(); /* the character that maps to a given character */ extern long imap_ord(); /* the ordinal number of a given character */ @y extern char imap_chr(long); /* the character that maps to a given character */ extern long imap_ord(char); /* the ordinal number of a given character */ @z @x l.213 static void icode_setup() @y static void icode_setup(void) @z @x l.225 extern void gb_newline(); /* advance to next line of the data file */ extern long new_checksum(); /* compute change in magic number */ @y extern void gb_newline(void); /* advance to next line of the data file */ extern long new_checksum(char *,long); /* compute change in magic number */ @z @x l.232 long new_checksum(s,old_checksum) char *s; /* a string */ long old_checksum; @y long new_checksum(@t\1\1@> char *s, /* a string */ long old_checksum@t\2\2@>) @z @x l.245 void gb_newline() @y void gb_newline(void) @z @x l.259 extern long gb_eof(); /* has the data all been read? */ @y extern long gb_eof(void); /* has the data all been read? */ @z @x l.260 long gb_eof() { return !more_data; } @y long gb_eof(void) { return !more_data; } @z @x l.276 extern char gb_char(); /* get next character of current line, or |'\n'| */ extern void gb_backup(); /* move back ready to scan a character again */ @y extern char gb_char(void); /* get next character of current line, or |'\n'| */ extern void gb_backup(void); /* move back ready to scan a character again */ @z @x l.280 char gb_char() @y char gb_char(void) @z @x l.286 void gb_backup() @y void gb_backup(void) @z @x l.307 extern long gb_digit(); /* |gb_digit(d)| reads a digit between 0 and |d-1| */ extern unsigned long gb_number(); /* |gb_number(d)| reads a radix-|d| number */ @y extern long gb_digit(char); /* |gb_digit(d)| reads a digit between 0 and |d-1| */ extern unsigned long gb_number(char); /* |gb_number(d)| reads a radix-|d| number */ @z @x l.315 long gb_digit(d) char d; @y long gb_digit(char d) @z @x l.322 unsigned long gb_number(d) char d; @y unsigned long gb_number(char d) @z @x l.353 extern char *gb_string(); /* |gb_string(p,c)| reads a string delimited by |c| @y extern char *gb_string(char *,char); /* |gb_string(p,c)| reads a string delimited by |c| @z @x l.360 char *gb_string(p,c) char *p; /* where to put the result */ char c; /* character following the string */ @y char *gb_string(@t\1\1@> char *p, /* where to put the result */ char c@t\2\2@>) /* character following the string */ @z @x l.427 extern void gb_raw_open(); /* open a file for GraphBase input */ extern long gb_open(); /* open a GraphBase data file; return 0 if OK */ @y extern void gb_raw_open(char *); /* open a file for GraphBase input */ extern long gb_open(char *); /* open a GraphBase data file; return 0 if OK */ @z @x l.431 void gb_raw_open(f) char *f; @y void gb_raw_open(char *f) @z @x l.463 long gb_open(f) char *f; @y long gb_open(char *f) @z @x l.534 long gb_close() @y long gb_close(void) @z @x l.567 extern long gb_close(); /* close a GraphBase data file; return 0 if OK */ extern long gb_raw_close(); /* close file and return the checksum */ @y extern long gb_close(void); /* close a GraphBase data file; return 0 if OK */ extern long gb_raw_close(void); /* close file and return the checksum */ @z @x l.571 long gb_raw_close() @y long gb_raw_close(void) @z �������������������������������������������������������������������������������������������PROTOTYPES/gb_lisa.ch�������������������������������������������������������������������������������0000444�0001750�0001750�00000006424�05635275732�012666� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.21 extern long* lisa(); extern Graph *plane_lisa(); extern Graph *bi_lisa(); @y extern long* lisa(unsigned long,unsigned long,unsigned long,@| unsigned long,unsigned long,unsigned long,unsigned long,@| unsigned long,unsigned long,Area);@/ extern Graph *plane_lisa(unsigned long,unsigned long,unsigned long,@| unsigned long,unsigned long,unsigned long,@| unsigned long,unsigned long,unsigned long); extern Graph *bi_lisa(unsigned long,unsigned long,@| unsigned long,unsigned long,unsigned long,unsigned long,@| unsigned long,long); @z @x l.149 long *lisa(m,n,d,m0,m1,n0,n1,d0,d1,area) unsigned long m,n; /* number of rows and columns desired */ unsigned long d; /* maximum pixel value desired */ unsigned long m0,m1; /* input will be from rows $[|m0|\,.\,.\,|m1|)$ */ unsigned long n0,n1; /* and from columns $[|n0|\,.\,.\,|n1|)$ */ unsigned long d0,d1; /* lower and upper threshold of raw pixel scores */ Area area; /* where to allocate the matrix that will be output */ @y long *lisa(@t\1\1@> unsigned long m,unsigned long n, /* number of rows and columns desired */ unsigned long d, /* maximum pixel value desired */ unsigned long m0,unsigned long m1, /* input will be from rows $[|m0|\,.\,.\,|m1|)$ */ unsigned long n0,unsigned long n1, /* and from columns $[|n0|\,.\,.\,|n1|)$ */ unsigned long d0,unsigned long d1, /* lower and upper threshold of raw pixel scores */ Area area@t\2\2@>) /* where to allocate the matrix that will be output */ @z @x l.286 static long na_over_b(n,a,b) long n,a,b; @y static long na_over_b(long n,long a,long b) @z @x l.405 @p Graph *plane_lisa(m,n,d,m0,m1,n0,n1,d0,d1) unsigned long m,n; /* number of rows and columns desired */ unsigned long d; /* maximum value desired */ unsigned long m0,m1; /* input will be from rows $[|m0|\,.\,.\,|m1|)$ */ unsigned long n0,n1; /* and from columns $[|n0|\,.\,.\,|n1|)$ */ unsigned long d0,d1; /* lower and upper threshold of raw pixel scores */ @y @p Graph *plane_lisa(@t\1\1@> unsigned long m,unsigned long n, /* number of rows and columns desired */ unsigned long d, /* maximum value desired */ unsigned long m0,unsigned long m1, /* input will be from rows $[|m0|\,.\,.\,|m1|)$ */ unsigned long n0,unsigned long n1, /* and from columns $[|n0|\,.\,.\,|n1|)$ */ unsigned long d0,unsigned long d1@t\2\2@>) /* lower and upper threshold of raw pixel scores */ @z @x l.562 static void adjac(u,v) Vertex *u,*v; @y static void adjac(Vertex *u,Vertex *v) @z @x l.591 @p Graph *bi_lisa(m,n,m0,m1,n0,n1,thresh,c) unsigned long m,n; /* number of rows and columns desired */ unsigned long m0,m1; /* input will be from rows $[|m0|\,.\,.\,|m1|)$ */ unsigned long n0,n1; /* and from columns $[|n0|\,.\,.\,|n1|)$ */ unsigned long thresh; /* threshold defining adjacency */ long c; /* should we prefer dark pixels to light pixels? */ @y @p Graph *bi_lisa(@t\1\1@> unsigned long m,unsigned long n, /* number of rows and columns desired */ unsigned long m0,unsigned long m1, /* input will be from rows $[|m0|\,.\,.\,|m1|)$ */ unsigned long n0,unsigned long n1, /* and from columns $[|n0|\,.\,.\,|n1|)$ */ unsigned long thresh, /* threshold defining adjacency */ long c@t\2\2@>) /* should we prefer dark pixels to light pixels? */ @z ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_plane.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000007404�07224007063�013016� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.20 extern Graph *plane(); extern Graph *plane_miles(); extern void delaunay(); @y extern Graph *plane(unsigned long,unsigned long,unsigned long,@| unsigned long,unsigned long,long); extern Graph *plane_miles(unsigned long,long,long,long,@| unsigned long,unsigned long,long); extern void delaunay(Graph *,void (*)(Vertex *,Vertex *)); @z @x l.92 Graph *plane(n,x_range,y_range,extend,prob,seed) unsigned long n; /* number of vertices desired */ unsigned long x_range,y_range; /* upper bounds on rectangular coordinates */ unsigned long extend; /* should a point at infinity be included? */ unsigned long prob; /* probability of rejecting a Delaunay edge */ long seed; /* random number seed */ @y Graph *plane(@t\1\1@> unsigned long n, /* number of vertices desired */ unsigned long x_range,unsigned long y_range, /* upper bounds on rectangular coordinates */ unsigned long extend, /* should a point at infinity be included? */ unsigned long prob, /* probability of rejecting a Delaunay edge */ long seed@t\2\2@>) /* random number seed */ @z @x l.226 void delaunay(g,f) Graph *g; /* vertices in the plane */ void @[@] (*f)(); /* procedure that absorbs the triangulated edges */ @y void delaunay(@t\1\1@> Graph *g, /* vertices in the plane */ void (*f)(Vertex *,Vertex *)@t\2\2@>) /* procedure that absorbs the triangulated edges */ @z @x l.252 static void new_euclid_edge(u,v) Vertex *u,*v; @y static void new_euclid_edge(Vertex *u,Vertex *v) @z @x l.283 static long int_sqrt(x) long x; @y static long int_sqrt(long x) @z @x l.322 static long sign_test(x1,x2,x3,y1,y2,y3) long x1,x2,x3,y1,y2,y3; @y static long sign_test(long x1,long x2,long x3,long y1,long y2,long y3) @z @x l.431 static long ccw(u,v,w) Vertex *u,*v,*w; @y static long ccw(Vertex *u,Vertex *v,Vertex *w) @z @x l.474 static long incircle(t,u,v,w) Vertex *t,*u,*v,*w; @y static long incircle(Vertex *t,Vertex *u,Vertex *v,Vertex *w) @z @x l.542 static long ff(t,u,v,w) Vertex *t,*u,*v,*w; @y static long ff(Vertex *t,Vertex *u,Vertex *v,Vertex *w) @z @x l.550 static long gg(t,u,v,w) Vertex *t,*u,*v,*w; @y static long gg(Vertex *t,Vertex *u,Vertex *v,Vertex *w) @z @x l.558 static long hh(t,u,v,w) Vertex *t,*u,*v,*w; @y static long hh(Vertex *t,Vertex *u,Vertex *v,Vertex *w) @z @x l.563 static long jj(t,u,v,w) Vertex *t,*u,*v,*w; @y static long jj(Vertex *t,Vertex *u,Vertex *v,Vertex *w) @z @x l.882 static void flip(c,d,e,t,tp,tpp,p,xp,xpp) arc *c,*d,*e; Vertex *t,*tp,*tpp,*p; node *xp,*xpp; @y static void flip(arc *c,arc *d,arc *e,@| Vertex *t,Vertex *tp,Vertex *tpp,Vertex *p,@| node *xp,node *xpp) @z @x l.931 Graph *plane_miles(n,north_weight,west_weight,pop_weight,extend,prob,seed) unsigned long n; /* number of vertices desired */ long north_weight; /* coefficient of latitude in the weight function */ long west_weight; /* coefficient of longitude in the weight function */ long pop_weight; /* coefficient of population in the weight function */ unsigned long extend; /* should a point at infinity be included? */ unsigned long prob; /* probability of rejecting a Delaunay edge */ long seed; /* random number seed */ @y Graph *plane_miles(@t\1\1@> unsigned long n, /* number of vertices desired */ long north_weight, /* coefficient of latitude in the weight function */ long west_weight, /* coefficient of longitude in the weight function */ long pop_weight, /* coefficient of population in the weight function */ unsigned long extend, /* should a point at infinity be included? */ unsigned long prob, /* probability of rejecting a Delaunay edge */ long seed@t\2\2@>) /* random number seed */ @z @x l.982 static void new_mile_edge(u,v) Vertex *u,*v; @y static void new_mile_edge(Vertex *u,Vertex *v) @z ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/test_sample.ch���������������������������������������������������������������������������0000444�0001750�0001750�00000003651�05716657345�013611� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.32 @t\4@>int main() @y int main(void) @z @x l.165 static void pr_vert(); /* a subroutine for printing a vertex is declared below */ static void pr_arc(); /* likewise for arcs */ static void pr_util(); /* and for utility fields in general */ static void print_sample(g,n) Graph *g; /* graph to be sampled and destroyed */ int n; /* index to the sampled vertex */ @y static void pr_vert(Vertex *,int,char *); /* a subroutine for printing a vertex is declared below */ static void pr_arc(Arc *,int,char *); /* likewise for arcs */ static void pr_util(util,char,int,char *); /* and for utility fields in general */ static void print_sample(@t\1\1@> Graph *g, /* graph to be sampled and destroyed */ int n@t\2\2@>) /* index to the sampled vertex */ @z @x l.191 static void pr_vert(v,l,s) Vertex *v; /* vertex to be printed */ int l; /* |<=0| if the output should be terse */ char *s; /* format for graph utility fields */ @y static void pr_vert(@t\1\1@> Vertex *v, /* vertex to be printed */ int l, /* |<=0| if the output should be terse */ char *s@t\2\2@>) /* format for graph utility fields */ @z @x l.216 static void pr_arc(a,l,s) Arc *a; /* non-null arc to be printed */ int l; /* |<=0| if the output should be terse */ char *s; /* format for graph utility fields */ @y static void pr_arc(@t\1\1@> Arc *a, /* non-null arc to be printed */ int l, /* |<=0| if the output should be terse */ char *s@t\2\2@>) /* format for graph utility fields */ @z @x l.231 static void pr_util(u,c,l,s) util u; /* a utility field to be printed */ char c; /* its type code */ int l; /* 0 if output should be terse, |-1| if pointers omitted */ char *s; /* utility types for overall graph */ @y static void pr_util(@t\1\1@> util u, /* a utility field to be printed */ char c, /* its type code */ int l, /* 0 if output should be terse, |-1| if pointers omitted */ char *s@t\2\2@>) /* utility types for overall graph */ @z ���������������������������������������������������������������������������������������PROTOTYPES/gb_save.ch�������������������������������������������������������������������������������0000444�0001750�0001750�00000003474�05635275732�012676� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.47 extern long save_graph(); extern Graph *restore_graph(); @y extern long save_graph(Graph *,char *); extern Graph *restore_graph(char *); @z @x l.149 Graph *restore_graph(f) char *f; /* the file name */ @y Graph *restore_graph(char *f) /* the file name */ @z @x l.225 static long fill_field(l,t) util *l; /* location of field to be filled in */ char t; /* its type code */ @y static long fill_field(@t\1\1@> util *l, /* location of field to be filled in */ char t@t\2\2@>) /* its type code */ @z @x l.308 static long finish_record() @y static long finish_record(void) @z @x l.410 long save_graph(g,f) Graph *g; /* graph to be saved */ char *f; /* name of the file to be created */ @y long save_graph(@t\1\1@> Graph *g, /* graph to be saved */ char *f@t\2\2@>) /* name of the file to be created */ @z @x l.518 static void classify(l,t) util *l; /* location of field to be classified */ char t; /* its type code, from the set $\{\.Z,\.I,\.V,\.S,\.A\}$ */ @y static void classify(@t\1\1@> util *l, /* location of field to be classified */ char t@t\2\2@>) /* its type code, from the set $\{\.Z,\.I,\.V,\.S,\.A\}$ */ @z @x l.672 static void flushout() /* output the buffer to |save_file| */ @y static void flushout(void) /* output the buffer to |save_file| */ @z @x l.686 static void prepare_string(s) char *s; /* string that is moved to |item_buf| */ @y static void prepare_string(char *s) /* string that is moved to |item_buf| */ @z @x l.709 static void move_item() @y static void move_item(void) @z @x l.747 static void translate_field(l,t) util *l; /* address of field to be output in symbolic form */ char t; /* type of formatting desired */ @y static void translate_field(@t\1\1@> util *l, /* address of field to be output in symbolic form */ char t@t\2\2@>) /* type of formatting desired */ @z ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/book_components.ch�����������������������������������������������������������������������0000444�0001750�0001750�00000001222�05635275732�014454� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.58 main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ @y int main(@t\1\1@> int argc, /* the number of command-line arguments */ char *argv[]@t\2\2@>) /* an array of strings containing those arguments */ @z @x l.111 char *vertex_name(v,i) /* return (as a string) the name of vertex |v| */ Vertex *v; char i; /* |i| should be 0, 1, or 2 to avoid clash in |code_name| array */ @y char *vertex_name(@t\1\1@> /* return (as a string) the name of vertex |v| */ Vertex *v, char i@t\2\2@>) /* |i| should be 0, 1, or 2 to avoid clash in |code_name| array */ @z ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/take_risc.ch�����������������������������������������������������������������������������0000444�0001750�0001750�00000000460�05716657246�013230� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.35 main(argc,argv) int argc; /* the number of command-line arguments */ char *argv[]; /* an array of strings containing those arguments */ @y int main(@t\1\1@> int argc, /* the number of command-line arguments */ char *argv[]@t\2\2@>) /* an array of strings containing those arguments */ @z ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/README.PROTOTYPES������������������������������������������������������������������������0000444�0001750�0001750�00000007450�07224011632�013340� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This file, ./PROTOTYPES/README.PROTOTYPES, is NOT part of the standard distribution of the Stanford GraphBase. COPYRIGHT NOTICE FOR ./PROTOTYPES The following copyright notice extends to all files in the ./PROTOTYPES subdirectory, but not to any part of the standard distribution of the Stanford GraphBase (which is copyright (c) 1993 by Stanford University). Copyright (c) 1994, 1996 Andreas Scherer Permission is granted to make and distribute verbatim copies of this document provided that the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this document under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. PURPOSE OF THIS FILE It describes the contents of the subdirectory ./PROTOTYPES, where `.' denotes the root directory of the standard installation of SGB. PURPOSE OF ./PROTOTYPES The additional subdirectory ./PROTOTYPES contains a set of change files for improved ANSI support, including all changes by Marc van Leeuwen as provided in the ./ANSI subdirectory, by implementing function declarations and function definitions in the form of `prototypes'. Normally I dislike to ignore or suppress warning messages from the compiler and/or the linker. For the sake of simplicity, the first attempt to install SGB on the Commodore AMIGA resulted in the ./AMIGA subdirectory, which contains only two (2) extra files. BUT: The CFLAGS macro in ./AMIGA/SMakefile has the entry "IGNORE=85+93+100+132+154+161" because of the missing prototypes in the original source files. The independent change files in ./PROTOTYPES take care of all these omissions, thus reducing the IGNORE variable to the single value `93'; this effects the section @<Vanilla local variables@> in ./gb_basic.w only. The patches in ./PROTOTYPES effect all of the kernel and library modules and some demonstration programs. To make use of these patches a special version of ./AMIGA/SMakefile is provided (development and test of these patches were done on a Commodore AMIGA with the help of SAS/C++ 6.56). ./PROTOTYPES/assign_lisa.ch ./PROTOTYPES/book_components.ch ./PROTOTYPES/econ_order.ch ./PROTOTYPES/football.ch ./PROTOTYPES/gb_basic.ch ./PROTOTYPES/gb_books.ch ./PROTOTYPES/gb_dijk.ch ./PROTOTYPES/gb_econ.ch ./PROTOTYPES/gb_flip.ch ./PROTOTYPES/gb_games.ch ./PROTOTYPES/gb_gates.ch ./PROTOTYPES/gb_graph.ch ./PROTOTYPES/gb_io.ch ./PROTOTYPES/gb_lisa.ch ./PROTOTYPES/gb_miles.ch ./PROTOTYPES/gb_plane.ch ./PROTOTYPES/gb_raman.ch ./PROTOTYPES/gb_rand.ch ./PROTOTYPES/gb_roget.ch ./PROTOTYPES/gb_save.ch ./PROTOTYPES/gb_sort.ch ./PROTOTYPES/gb_words.ch ./PROTOTYPES/girth.ch ./PROTOTYPES/ladders.ch ./PROTOTYPES/miles_span.ch ./PROTOTYPES/multiply.ch ./PROTOTYPES/queen.ch ./PROTOTYPES/README.PROTOTYPES ./PROTOTYPES/roget_components.ch ./PROTOTYPES/SMakefile ./PROTOTYPES/take_risc.ch ./PROTOTYPES/test_sample.ch ./PROTOTYPES/word_components.ch HOW TO USE THESE PATCHES Copy the contents of ./PROTOTYPES to the root directory of the standard SGB installation. Care has been taken to avoid any collisions with existing files in the root directory. Then say "make tests", "make install", "make installdemos", and "make clean", in this order as described in ./README. To date this holds for AMIGA users only, but support of other operating systems and compilers is easy; the original UNIX ./Makefile already contains provisions for the presence of change files and will acknowledge the files from ./PROTOTYPES automatically. TROUBLE SHOOTING Should you encounter problems with this patch for SGB or should you have ideas for further improvements contact the author of this contribution Andreas Scherer Rochusstraße 22-24 52062 Aachen, Germany <andreas.scherer@pobox.com> ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PROTOTYPES/gb_words.ch������������������������������������������������������������������������������0000444�0001750�0001750�00000002207�05635275732�013067� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@x l.17 extern Graph *words(); extern Vertex *find_word(); @y extern Graph *words(unsigned long,long [],long,long); extern Vertex *find_word(char *,void (*)(Vertex *)); @z @x l.162 Graph *words(n,wt_vector,wt_threshold,seed) unsigned long n; /* maximum number of vertices desired */ long wt_vector[]; /* pointer to array of weights */ long wt_threshold; /* minimum qualifying weight */ long seed; /* random number seed */ @y Graph *words(@t\1\1@> unsigned long n, /* maximum number of vertices desired */ long wt_vector[], /* pointer to array of weights */ long wt_threshold, /* minimum qualifying weight */ long seed@t\2\2@>) /* random number seed */ @z @x l.210 static double flabs(x) long x; @y static double flabs(long x) @z @x l.256 static long iabs(x) long x; @y static long iabs(long x) @z @x l.508 @p Vertex *find_word(q,f) char *q; void @[@] (*f)(); /* |*f| should take one argument, of type |Vertex *|, or |f| should be |NULL| */ @y @p Vertex *find_word(@t\1\1@> char *q,void (*f)(Vertex *)@t\2\2@>) /* |*f| should take one argument, of type |Vertex *|, or |f| should be |NULL| */ @z �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/�����������������������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�10602556207�007753� 5����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/econ_order.dsp���������������������������������������������������������������������������������0000444�0001750�0001750�00000011172�07224576577�012624� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="econ_order" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=econ_order - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "econ_order.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "econ_order.mak" CFG="econ_order - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "econ_order - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "econ_order - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "econ_order - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "econ_order - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "econ_order___Win32_Debug" # PROP BASE Intermediate_Dir "econ_order___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "econ_order - Win32 Release" # Name "econ_order - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\econ_order.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\econ_order.w !IF "$(CFG)" == "econ_order - Win32 Release" # Begin Custom Build InputPath=..\econ_order.w InputName=econ_order "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "econ_order - Win32 Debug" # Begin Custom Build InputPath=..\econ_order.w InputName=econ_order "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/tests.dsp��������������������������������������������������������������������������������������0000444�0001750�0001750�00000003630�06757224116�011634� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="tests" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Generic Project" 0x010a CFG=tests - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "tests.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "tests.mak" CFG="tests - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "tests - Win32 Release" (based on "Win32 (x86) Generic Project") !MESSAGE "tests - Win32 Debug" (based on "Win32 (x86) Generic Project") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" MTL=midl.exe !IF "$(CFG)" == "tests - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD MTL /D "SYSV" !ELSEIF "$(CFG)" == "tests - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "tests___Win32_Debug" # PROP BASE Intermediate_Dir "tests___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "" # PROP Intermediate_Dir "" # PROP Target_Dir "" # Begin Special Build Tool SOURCE="$(InputPath)" PostBuild_Cmds=test_io test_flip test_graph test_sample # End Special Build Tool !ENDIF # Begin Target # Name "tests - Win32 Release" # Name "tests - Win32 Debug" # End Target # End Project ��������������������������������������������������������������������������������������������������������MSVC/assign_lisa.dsp��������������������������������������������������������������������������������0000444�0001750�0001750�00000011217�07166607754�012776� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="assign_lisa" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=assign_lisa - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "assign_lisa.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "assign_lisa.mak" CFG="assign_lisa - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "assign_lisa - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "assign_lisa - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "assign_lisa - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "assign_lisa - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "assign_lisa___Win32_Debug" # PROP BASE Intermediate_Dir "assign_lisa___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "assign_lisa - Win32 Release" # Name "assign_lisa - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\assign_lisa.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\assign_lisa.w !IF "$(CFG)" == "assign_lisa - Win32 Release" # Begin Custom Build InputPath=..\assign_lisa.w InputName=assign_lisa "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "assign_lisa - Win32 Debug" # Begin Custom Build InputPath=..\assign_lisa.w InputName=assign_lisa "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/roget_components.dsp���������������������������������������������������������������������������0000444�0001750�0001750�00000011370�07224576577�014072� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="roget_components" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=roget_components - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "roget_components.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "roget_components.mak" CFG="roget_components - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "roget_components - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "roget_components - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "roget_components - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "roget_components - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "roget_components___Win32_Debug" # PROP BASE Intermediate_Dir "roget_components___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "roget_components - Win32 Release" # Name "roget_components - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\roget_components.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\roget_components.w !IF "$(CFG)" == "roget_components - Win32 Release" # Begin Custom Build InputPath=..\roget_components.w InputName=roget_components "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "roget_components - Win32 Debug" # Begin Custom Build InputPath=..\roget_components.w InputName=roget_components "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/word_components.dsp����������������������������������������������������������������������������0000444�0001750�0001750�00000011343�07224576577�013725� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="word_components" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=word_components - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "word_components.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "word_components.mak" CFG="word_components - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "word_components - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "word_components - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "word_components - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "word_components - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "word_components___Win32_Debug" # PROP BASE Intermediate_Dir "word_components___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "word_components - Win32 Release" # Name "word_components - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\word_components.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\word_components.w !IF "$(CFG)" == "word_components - Win32 Release" # Begin Custom Build InputPath=..\word_components.w InputName=word_components "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "word_components - Win32 Debug" # Begin Custom Build InputPath=..\word_components.w InputName=word_components "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/ladders.dsp������������������������������������������������������������������������������������0000444�0001750�0001750�00000011073�07224576577�012123� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="ladders" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=ladders - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "ladders.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "ladders.mak" CFG="ladders - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "ladders - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "ladders - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "ladders - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "ladders - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "ladders___Win32_Debug" # PROP BASE Intermediate_Dir "ladders___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "ladders - Win32 Release" # Name "ladders - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\ladders.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\ladders.w !IF "$(CFG)" == "ladders - Win32 Release" # Begin Custom Build InputPath=..\ladders.w InputName=ladders "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "ladders - Win32 Debug" # Begin Custom Build InputPath=..\ladders.w InputName=ladders "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/libgb.dsp��������������������������������������������������������������������������������������0000444�0001750�0001750�00000037643�07166607754�011574� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="libgb" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Static Library" 0x0104 CFG=libgb - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "libgb.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "libgb.mak" CFG="libgb - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "libgb - Win32 Release" (based on "Win32 (x86) Static Library") !MESSAGE "libgb - Win32 Debug" (based on "Win32 (x86) Static Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "libgb - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LIB32=link.exe -lib # ADD BASE LIB32 /nologo # ADD LIB32 /nologo !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LIB32=link.exe -lib # ADD BASE LIB32 /nologo # ADD LIB32 /nologo !ENDIF # Begin Target # Name "libgb - Win32 Release" # Name "libgb - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\gb_basic.c # End Source File # Begin Source File SOURCE=.\gb_books.c # End Source File # Begin Source File SOURCE=.\gb_dijk.c # End Source File # Begin Source File SOURCE=.\gb_econ.c # End Source File # Begin Source File SOURCE=.\gb_flip.c # End Source File # Begin Source File SOURCE=.\gb_games.c # End Source File # Begin Source File SOURCE=.\gb_gates.c # End Source File # Begin Source File SOURCE=.\gb_graph.c # End Source File # Begin Source File SOURCE=.\gb_io.c # End Source File # Begin Source File SOURCE=.\gb_lisa.c # End Source File # Begin Source File SOURCE=.\gb_miles.c # End Source File # Begin Source File SOURCE=.\gb_plane.c # End Source File # Begin Source File SOURCE=.\gb_raman.c # End Source File # Begin Source File SOURCE=.\gb_rand.c # End Source File # Begin Source File SOURCE=.\gb_roget.c # End Source File # Begin Source File SOURCE=.\gb_save.c # End Source File # Begin Source File SOURCE=.\gb_sort.c # End Source File # Begin Source File SOURCE=.\gb_words.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\gb_basic.h # End Source File # Begin Source File SOURCE=.\gb_books.h # End Source File # Begin Source File SOURCE=.\gb_dijk.h # End Source File # Begin Source File SOURCE=.\gb_econ.h # End Source File # Begin Source File SOURCE=.\gb_flip.h # End Source File # Begin Source File SOURCE=.\gb_games.h # End Source File # Begin Source File SOURCE=.\gb_gates.h # End Source File # Begin Source File SOURCE=.\gb_graph.h # End Source File # Begin Source File SOURCE=.\gb_io.h # End Source File # Begin Source File SOURCE=.\gb_lisa.h # End Source File # Begin Source File SOURCE=.\gb_miles.h # End Source File # Begin Source File SOURCE=.\gb_plane.h # End Source File # Begin Source File SOURCE=.\gb_raman.h # End Source File # Begin Source File SOURCE=.\gb_rand.h # End Source File # Begin Source File SOURCE=.\gb_roget.h # End Source File # Begin Source File SOURCE=.\gb_save.h # End Source File # Begin Source File SOURCE=.\gb_sort.h # End Source File # Begin Source File SOURCE=.\gb_words.h # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\gb_basic.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_basic.w InputName=gb_basic "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_basic.w InputName=gb_basic "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_books.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_books.w InputName=gb_books "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_books.w InputName=gb_books "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_dijk.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_dijk.w InputName=gb_dijk "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_dijk.w InputName=gb_dijk "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_econ.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_econ.w InputName=gb_econ "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_econ.w InputName=gb_econ "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_flip.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_flip.w InputName=gb_flip "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_flip.w InputName=gb_flip "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_games.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_games.w InputName=gb_games "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_games.w InputName=gb_games "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_gates.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_gates.w InputName=gb_gates "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_gates.w InputName=gb_gates "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_graph.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_graph.w InputName=gb_graph "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_graph.w InputName=gb_graph "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_io.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_io.w InputName=gb_io "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_io.w InputName=gb_io "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_lisa.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_lisa.w InputName=gb_lisa "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_lisa.w InputName=gb_lisa "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_miles.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_miles.w InputName=gb_miles "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_miles.w InputName=gb_miles "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_plane.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_plane.w InputName=gb_plane "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_plane.w InputName=gb_plane "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_raman.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_raman.w InputName=gb_raman "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_raman.w InputName=gb_raman "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_rand.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_rand.w InputName=gb_rand "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_rand.w InputName=gb_rand "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_roget.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_roget.w InputName=gb_roget "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_roget.w InputName=gb_roget "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_save.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_save.w InputName=gb_save "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_save.w InputName=gb_save "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_sort.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_sort.w InputName=gb_sort "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_sort.w InputName=gb_sort "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\gb_words.w !IF "$(CFG)" == "libgb - Win32 Release" # Begin Custom Build InputPath=..\gb_words.w InputName=gb_words "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "libgb - Win32 Debug" # Begin Custom Build InputPath=..\gb_words.w InputName=gb_words "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ���������������������������������������������������������������������������������������������MSVC/test_graph.dsp���������������������������������������������������������������������������������0000444�0001750�0001750�00000007656�06757224116�012646� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="test_graph" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=test_graph - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "test_graph.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "test_graph.mak" CFG="test_graph - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "test_graph - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "test_graph - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "test_graph - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "test_graph - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "test_graph___Win32_Debug" # PROP BASE Intermediate_Dir "test_graph___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "test_graph - Win32 Release" # Name "test_graph - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\test_graph.c # End Source File # End Group # End Target # End Project ����������������������������������������������������������������������������������MSVC/multiply.dsp�����������������������������������������������������������������������������������0000444�0001750�0001750�00000011120�07224576577�012355� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="multiply" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=multiply - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "multiply.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "multiply.mak" CFG="multiply - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "multiply - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "multiply - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "multiply - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "multiply - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "multiply___Win32_Debug" # PROP BASE Intermediate_Dir "multiply___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "multiply - Win32 Release" # Name "multiply - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\multiply.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\multiply.w !IF "$(CFG)" == "multiply - Win32 Release" # Begin Custom Build InputPath=..\multiply.w InputName=multiply "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "multiply - Win32 Debug" # Begin Custom Build InputPath=..\multiply.w InputName=multiply "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/queen.dsp��������������������������������������������������������������������������������������0000444�0001750�0001750�00000011021�07224576577�011613� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="queen" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=queen - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "queen.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "queen.mak" CFG="queen - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "queen - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "queen - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "queen - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "queen - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "queen___Win32_Debug" # PROP BASE Intermediate_Dir "queen___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "queen - Win32 Release" # Name "queen - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\queen.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\queen.w !IF "$(CFG)" == "queen - Win32 Release" # Begin Custom Build InputPath=..\queen.w InputName=queen "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "queen - Win32 Debug" # Begin Custom Build InputPath=..\queen.w InputName=queen "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/test_sample.dsp��������������������������������������������������������������������������������0000444�0001750�0001750�00000011217�07224576577�013025� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="test_sample" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=test_sample - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "test_sample.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "test_sample.mak" CFG="test_sample - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "test_sample - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "test_sample - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "test_sample - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "test_sample - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "test_sample___Win32_Debug" # PROP BASE Intermediate_Dir "test_sample___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "test_sample - Win32 Release" # Name "test_sample - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\test_sample.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\test_sample.w !IF "$(CFG)" == "test_sample - Win32 Release" # Begin Custom Build InputPath=..\test_sample.w InputName=test_sample "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "test_sample - Win32 Debug" # Begin Custom Build InputPath=..\test_sample.w InputName=test_sample "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/girth.dsp��������������������������������������������������������������������������������������0000444�0001750�0001750�00000011021�07224576577�011613� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="girth" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=girth - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "girth.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "girth.mak" CFG="girth - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "girth - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "girth - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "girth - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "girth - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "girth___Win32_Debug" # PROP BASE Intermediate_Dir "girth___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "girth - Win32 Release" # Name "girth - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\girth.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\girth.w !IF "$(CFG)" == "girth - Win32 Release" # Begin Custom Build InputPath=..\girth.w InputName=girth "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "girth - Win32 Debug" # Begin Custom Build InputPath=..\girth.w InputName=girth "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/take_risc.dsp����������������������������������������������������������������������������������0000444�0001750�0001750�00000011145�07224576577�012451� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="take_risc" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=take_risc - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "take_risc.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "take_risc.mak" CFG="take_risc - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "take_risc - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "take_risc - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "take_risc - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "take_risc - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "take_risc___Win32_Debug" # PROP BASE Intermediate_Dir "take_risc___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "take_risc - Win32 Release" # Name "take_risc - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\take_risc.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\take_risc.w !IF "$(CFG)" == "take_risc - Win32 Release" # Begin Custom Build InputPath=..\take_risc.w InputName=take_risc "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "take_risc - Win32 Debug" # Begin Custom Build InputPath=..\take_risc.w InputName=take_risc "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/README.MSVC������������������������������������������������������������������������������������0000444�0001750�0001750�00000011147�10601511025�011370� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This file, ./MSVC/README.MSVC, is NOT part of the standard distribution of the Stanford GraphBase. COPYRIGHT NOTICE FOR ./MSVC The following copyright notice extends to all files in the ./MSVC subdirectory, but not to any part of the standard distribution of the Stanford GraphBase (which is copyright (C) 1993 by Stanford University). Copyright (C) 1999, Andreas Scherer Permission is granted to make and distribute verbatim copies of this document provided that the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this document under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. PURPOSE OF THIS FILE The standard distribution of SGB is assumed to be installed on your computer. In the following we assume that `computer' is some general-purpose calculating device running any operating system that supports the Microsoft Visual C++ 6.0 compiler, which is the mandatory tool for using the files described here. `Installed' means that all source files from the original archive are present in a root directory (denoted by `.' in this file) and, if appropriate, one or more subdirectories, on your machine. If you don't have SGB yet, go out and get it from ftp.cs.stanford.edu, where it resides in the directory `~ftp/pub/sgb', or possibly from some other host location. The original source files are totally left untouched (as this is expressly prohibited by the copyright notice included in every single file of SGB). For the installation, the test, and the usage of SGB on your computer, a set of 21 additional files is coming with this patch. They should be added to the local SGB installation in a new subdirectory `./MSVC'. ./MSVC/README.MSVC This file. ./MSVC/sgb.dsw `Developer Studio Workspace' for MSVC6, covering all `Developer Studio Projects.' ./MSVC/libgb.dsp `Developer Studio Project' for the proper SGB library used by all tests and examples. ./MSVC/tests.dsp `Developer Studio Project' covering the four test programs. ./MSVC/test_flip.dsp ./MSVC/test_graph.dsp ./MSVC/test_io.dsp ./MSVC/test_sample.dsp ./MSVC/examples.dsp `Developer Studio Project' covering the twelve example programs. ./MSVC/assign_lisa.dsp ./MSVC/book_components.dsp ./MSVC/econ_order.dsp ./MSVC/football.dsp ./MSVC/girth.dsp ./MSVC/ladders.dsp ./MSVC/miles_span.dsp ./MSVC/multiply.dsp ./MSVC/queen.dsp ./MSVC/roget_components.dsp ./MSVC/take_risc.dsp ./MSVC/word_components.dsp HOW TO INSTALL SGB FOR MSVC First read the ./README file that comes with the standard distribution of the Stanford GraphBase, but don't follow the instructions for building the targets yet. Some points are overwritten by what follows. Details of the installation process are described in ./README, though. Then install CWEB (version 3.0 or greater), which can be found in various archives; the master files reside at ftp.cs.stanford.edu; a complete Win32 installation of CWEB can be found at http://www.literateprogramming.com. Now launch Microsoft Visual C++ 6.0 by double-clicking on the file "sgb.dsw". Select the menu "Project/Settings" for "All Configurations" and set the "Working Directory" on the "Debug" panel to the value ".." and press "OK". Then choose "tests" as the active project and press <F7>. This will automagically compile four test programs for "The Stanford GraphBase". (Note that the CTANGLE executable must be found by MSVC in one of the "executable" paths specified in "Tools/Options/Directories".) Run the four test programs and compare the results with the expected output. Choose "examples" as the active project and press <F7> again. This will automagically compile the example programs shipped with "The Stanford GraphBase". You may, of course, compile each of the separate projects by setting it as the active project and pressing <F7>. TROUBLE SHOOTING This version of SGB was tested with the following setup: Pentium II/350 PC Microsoft Windows NT 4 (sp5) Microsoft Visual C++ 6.0 (sp3) CWEBbin 3.63 (patch level 19) Should you encounter problems with this MSVC port of SGB or should you have ideas for further improvements, contact the author of this contribution. Andreas Scherer Rochusstraße 22-24 52062 Aachen, Germany <andreas.scherer@pobox.com> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/miles_span.dsp���������������������������������������������������������������������������������0000444�0001750�0001750�00000011172�07224576577�012637� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="miles_span" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=miles_span - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "miles_span.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "miles_span.mak" CFG="miles_span - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "miles_span - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "miles_span - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "miles_span - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "miles_span - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "miles_span___Win32_Debug" # PROP BASE Intermediate_Dir "miles_span___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "miles_span - Win32 Release" # Name "miles_span - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\miles_span.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\miles_span.w !IF "$(CFG)" == "miles_span - Win32 Release" # Begin Custom Build InputPath=..\miles_span.w InputName=miles_span "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "miles_span - Win32 Debug" # Begin Custom Build InputPath=..\miles_span.w InputName=miles_span "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/examples.dsp�����������������������������������������������������������������������������������0000444�0001750�0001750�00000003471�06757224116�012313� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="examples" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Generic Project" 0x010a CFG=examples - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "examples.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "examples.mak" CFG="examples - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "examples - Win32 Release" (based on "Win32 (x86) Generic Project") !MESSAGE "examples - Win32 Debug" (based on "Win32 (x86) Generic Project") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" MTL=midl.exe !IF "$(CFG)" == "examples - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "" !ELSEIF "$(CFG)" == "examples - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "examples___Win32_Debug" # PROP BASE Intermediate_Dir "examples___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "" # PROP Intermediate_Dir "" # PROP Target_Dir "" # ADD MTL /D "SYSV" !ENDIF # Begin Target # Name "examples - Win32 Release" # Name "examples - Win32 Debug" # End Target # End Project �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/book_components.dsp����������������������������������������������������������������������������0000444�0001750�0001750�00000011263�07224576577�013705� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="book_components" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=book_components - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "book_components.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "book_components.mak" CFG="book_components - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "book_components - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "book_components - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "book_components - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "book_components - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "book_components - Win32 Release" # Name "book_components - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\book_components.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\book_components.w !IF "$(CFG)" == "book_components - Win32 Release" # Begin Custom Build InputPath=..\book_components.w InputName=book_components "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "book_components - Win32 Debug" # Begin Custom Build InputPath=..\book_components.w InputName=book_components "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/football.dsp�����������������������������������������������������������������������������������0000444�0001750�0001750�00000011120�07224576577�012300� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="football" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=football - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "football.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "football.mak" CFG="football - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "football - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "football - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "football - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "football - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "football___Win32_Debug" # PROP BASE Intermediate_Dir "football___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "football - Win32 Release" # Name "football - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\football.c # End Source File # End Group # Begin Group "CWEB Files" # PROP Default_Filter "w;ch" # Begin Source File SOURCE=..\football.w !IF "$(CFG)" == "football - Win32 Release" # Begin Custom Build InputPath=..\football.w InputName=football "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ELSEIF "$(CFG)" == "football - Win32 Debug" # Begin Custom Build InputPath=..\football.w InputName=football "$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" set CWEBINPUTS=.. ctangle ../$(InputName).w ../PROTOTYPES/$(InputName).ch # End Custom Build !ENDIF # End Source File # End Group # End Target # End Project ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/test_io.dsp������������������������������������������������������������������������������������0000444�0001750�0001750�00000007604�06757224116�012145� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="test_io" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=test_io - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "test_io.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "test_io.mak" CFG="test_io - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "test_io - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "test_io - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "test_io - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "test_io - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "test_io___Win32_Debug" # PROP BASE Intermediate_Dir "test_io___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "test_io - Win32 Release" # Name "test_io - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\test_io.c # End Source File # End Group # End Target # End Project ����������������������������������������������������������������������������������������������������������������������������MSVC/sgb.dsw����������������������������������������������������������������������������������������0000444�0001750�0001750�00000015301�06721276206�011250� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Microsoft Developer Studio Workspace File, Format Version 6.00 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! ############################################################################### Project: "assign_lisa"=.\assign_lisa.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "book_components"=.\book_components.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "econ_order"=.\econ_order.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "examples"=.\examples.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name assign_lisa End Project Dependency Begin Project Dependency Project_Dep_Name book_components End Project Dependency Begin Project Dependency Project_Dep_Name econ_order End Project Dependency Begin Project Dependency Project_Dep_Name football End Project Dependency Begin Project Dependency Project_Dep_Name girth End Project Dependency Begin Project Dependency Project_Dep_Name ladders End Project Dependency Begin Project Dependency Project_Dep_Name miles_span End Project Dependency Begin Project Dependency Project_Dep_Name multiply End Project Dependency Begin Project Dependency Project_Dep_Name queen End Project Dependency Begin Project Dependency Project_Dep_Name roget_components End Project Dependency Begin Project Dependency Project_Dep_Name take_risc End Project Dependency Begin Project Dependency Project_Dep_Name word_components End Project Dependency }}} ############################################################################### Project: "football"=.\football.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "girth"=.\girth.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "ladders"=.\ladders.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "libgb"=.\libgb.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Project: "miles_span"=.\miles_span.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "multiply"=.\multiply.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "queen"=.\queen.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "roget_components"=.\roget_components.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "take_risc"=.\take_risc.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "test_flip"=.\test_flip.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "test_graph"=.\test_graph.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "test_io"=.\test_io.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "test_sample"=.\test_sample.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Project: "tests"=.\tests.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name test_sample End Project Dependency Begin Project Dependency Project_Dep_Name test_io End Project Dependency Begin Project Dependency Project_Dep_Name test_flip End Project Dependency Begin Project Dependency Project_Dep_Name test_graph End Project Dependency }}} ############################################################################### Project: "word_components"=.\word_components.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ Begin Project Dependency Project_Dep_Name libgb End Project Dependency }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������MSVC/test_flip.dsp����������������������������������������������������������������������������������0000444�0001750�0001750�00000007640�06757224116�012470� 0����������������������������������������������������������������������������������������������������ustar �don�����������������������������don��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Microsoft Developer Studio Project File - Name="test_flip" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=test_flip - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "test_flip.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "test_flip.mak" CFG="test_flip - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "test_flip - Win32 Release" (based on "Win32 (x86) Console Application") !MESSAGE "test_flip - Win32 Debug" (based on "Win32 (x86) Console Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "test_flip - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "NDEBUG" # ADD RSC /l 0x407 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Release/libgb.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "test_flip - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "test_flip___Win32_Debug" # PROP BASE Intermediate_Dir "test_flip___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /MD /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "SYSV" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x407 /d "_DEBUG" # ADD RSC /l 0x407 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Debug/libgb.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "test_flip - Win32 Release" # Name "test_flip - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\test_flip.c # End Source File # End Group # End Target # End Project ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������