staticint punktzahl, maxpunktzahl, mod=0, rest=0, splitpunktzahl=0, splitzaehler= -1; staticint topsort; /* 0 for internal order, 1 for topological order */
/* die Levelnumerierung startet bei 0 -- das Ende von Listen wird durch eine 0 markiert. */ static graph subsets[MAXN][MAXGRAPH+1]; /* subsets[i]: Die Liste der direkten oberen Nachbarn auf level i */ staticint anzahl_subsets[MAXN]; /* die Laenge dieser Liste, d.h. die Anzahl der Moeglichkeiten
fuer DIREKTE obere Nachbarn*/
static graph nbs[MAXN][MAXGRAPH+1]; /* nbs[i-1]: Die Liste der moeglichen Nachbarschaften fuer Knoten auf
level i */ staticint anzahl_nbs[MAXN]; /* die Laenge dieser Liste */
static graph all_ancestors[MAXGRAPH+1]={0}; /* all_ancestors[i] sind alle Vorgaenger der Menge "i" als
binaere Menge (oder Graph) interpretiert */
static graph nonequiv_nbs[MAXN][MAXGRAPH+1]; /* hier werden die wirklich verschiedenen nbs gespeichert.
Haengt nicht vom level ab, sondern von der Punktzahl */ staticint maxorbsize_level[MAXN]; /* maxorbsize_level[i]: Wir gross ist der groesste Orbit der AutomorphismengruppefuerdasPoseteingeschraenktauflevel
0...i auf den Nachbarschaften ? */ static graph orbitnumber[MAXN][MAXGRAPH]; /* hier werden die Orbitindizees gespeichert, wenn ein
neues level begonnen wird */ staticint first_point_level[MAXN]; /* Die Nummer des kleinsten Knotens auf dem Level */
static graph *jump[MAXN][MAXN+1]; /* ist i auf level j, so ist jump[i][j] die erste nbs, die
j nicht als kleinstes Element hat */
#define FOREACH(i,set) for ((i)=elementlist[set]; *(i)!=MAXN; (i)++)
staticint points_on_level[MAXN]={0}; /* Die Anzahl der Knoten auf dem Level */ static graph poset[MAXN]={0}; static graph undirected_poset[MAXN]={0}; static graph all_points_up_to[MAXN]={0}; /* all_points_up_to[i] ist die Bitmap, die alle Knoten
bis level i enthaelt */ static graph all_points_on[MAXN]={0}; /* all_points_on[i] ist die Bitmap, die alle Knoten
auf level i enthaelt */
/* BDM char next_element[MAXGRAPH+1][MAXN+1]; * die Funktion nextelement() als Vektor */ staticchar elementlist[MAXGRAPH+1][MAXN+1]; /* Die Liste der Elemente */ /* BDM char first_bit[MAXGRAPH+1]; * das Makro FIRSTBIT als Vektor */
staticvoid writeposet(graph *g, int n) /* Write a poset to stdout, optionally in upper-triangular form */
{
graph h[MAXN]; int p[MAXN],pinv[MAXN]; int i,j,k;
setword left,new,w;
left = ALLMASK(n);
j = 0; while (left)
{
w = 0; for (i = 0; i < n; ++i) w |= h[i]; new = left & ~w;
left &= ~new; while (new)
{
TAKEBIT(k,new);
p[j++] = k;
h[k] = 0;
}
}
if (j != n) gt_abort(">E topsort error\n");
for (i = 0; i < n; ++i) pinv[p[i]] = i;
for (i = 0; i < n; ++i)
{
left = g[p[i]];
w = 0; while (left)
{
TAKEBIT(k,left);
w |= bit[pinv[k]];
}
h[i] = w;
}
staticvoid sammle_permutationen(int count, int perm[], int orbits[], int numorbits, int stabvertex, int n)
{
memcpy(generators+number_of_generators,perm,sizeof(int)*n);
transposition[number_of_generators] = 0;
staticvoid init_nauty() /* initialises the nauty variables */
{ /* tc_level = 0 is not the default in the most recent editionsofnauty.However,itisbetterforverysmall
graphs so we set it here. */
static boolean refinex(graph *g, int *lab, int *ptn, int *numcells, int *count, set *active, setword good_vertices, int n) /* custom version of refine1() which can exit quickly if required.
In all other cases, the refinement is completed and TRUE is returned. */
{ registerint i,c1,c2,labc1; register setword x,lact; int split1,split2,cell1,cell2; int cnt,bmin,bmax;
set *gptr;
setword workset; int workperm[MAXN]; int bucket[MAXN+2];
if (n == 1) returnTRUE;
lact = *active;
split1 = -1; while (*numcells < n && lact)
{
x = lact & (-lact);
split1 = FIRSTBIT(x);
lact ^= x;
static boolean call_nauty(int *lab, int *ptn, int n, int *orbits,
setword good_vertices, optionblk *options) /* Call nauty on undirected_poset, maybe just pretending. On exit, stats.numorbitsgivesthenumberoforbits.Otherfieldsofstats arenotnecessarilyset. Onlyn<=WORDSIZEissupported!
Ifoptions->getcanon==TRUE: Ifthecanonicallylastorbitcontainingvingood_vertices(mustexist!) alsocontainsvertexn-1,thenlab[]willbeincanonicalorderand thevalueTRUEisreturned.Alsoorbits[]iscorrect(ifnotNULL) andgeneratorsarestoredintransposition[]/generators[]if options->userautomproc!=NULL. ElsethevalueFALSEisreturned.Inthiscasenootherinformation aboutthegrouporlabellingcanbeassumedcorrect. Ifoptions->getcanon==FALSE: good_verticesisignored.lab[]isnotnecessarilyincanonicalorder. ThevalueTRUEisreturned.Alsoorbits[]iscorrect(ifnotNULL) andgeneratorsarestoredintransposition[]/generators[]if options->userautomproc!=NULL.
*/
{
setword active; int tmp_perm[MAXN]; int tmp_orbits[MAXN],*orbs; int i,j,tpos,tpos2,numcells; int a,b,c,d,which,min;
graph canong[MAXN];
boolean cells_are_orbits;
/* First call refinex(). This will perform a refinement of the partitionwhileatthesametimelookingforthepossibilityof
a quick reject or accept. */
active = 0;
numcells = 0; for (i = 0; i < n; ++i)
{
++numcells;
active |= bit[i]; while (ptn[i]) ++i;
}
if (!refinex(undirected_poset,lab,ptn,&numcells,tmp_perm,&active,
(options->getcanon?good_vertices:0),n)) returnFALSE;
/* The partition returned by refine1() is known from theory to be the orbitspartitioninmanycases.nautycontainsaprocedureforthis. Itincludesthecasewheretherearen,n-1orn-2cellsandinthose
cases we can easily find the group as well as just the orbits. */
cells_are_orbits = cheapautom(ptn,0,FALSE,n);
if (numcells == n)
{
++nauty_count1;
stats.numorbits = n; if (orbits) for (i = 0; i < n; ++i) orbits[i] = i;
/* lab is already in canonical order */
returnTRUE;
} elseif (numcells == n-1)
{ /* This is a transposition (ab). */
++nauty_count2; for (tpos = 0; ptn[tpos] == 0; ++tpos) {}
a = lab[tpos]; b = lab[tpos+1];
stats.numorbits = n-1;
if (options->userautomproc) /* Make the generator (ab). */
transposition[number_of_generators++] = bit[a]|bit[b];
if (orbits)
{ for (i = 0; i < n; ++i) orbits[i] = i; if (a > b) orbits[a] = b; else orbits[b] = a;
}
/* lab is already in canonical order */
returnTRUE;
} elseif (numcells == n-2)
{ /* There are three cases here. which=1:(ab)and(bc) which=2:(ab)and(cd)
which=3 : (ab)(cd) */
++nauty_count3;
stats.numorbits = n-2;
for (tpos = 0; ptn[tpos] == 0; ++tpos) {}
a = lab[tpos]; b = lab[tpos+1]; if (ptn[tpos+1] > 0)
{
which = 1;
c = lab[tpos+2];
} else
{ for (tpos2 = tpos+2; ptn[tpos2] == 0; ++tpos2) {}
c = lab[tpos2]; d = lab[tpos2+1]; if ((undirected_poset[a]&bit[c]) == (undirected_poset[b]&bit[c]))
which = 2; else
which = 3;
}
if (options->userautomproc)
{ if (which == 1) /* (ab) and (bc) */
{
transposition[number_of_generators++] = bit[a]|bit[b];
transposition[number_of_generators++] = bit[b]|bit[c];
} elseif (which == 2) /* (ab) and (cd) */
{
transposition[number_of_generators++] = bit[a]|bit[b];
transposition[number_of_generators++] = bit[c]|bit[d];
} else/* (ab)(cd) */
{ for (i = 0; i < n; ++i) tmp_perm[i] = i;
tmp_perm[a] = b; tmp_perm[b] = a;
tmp_perm[c] = d; tmp_perm[d] = c;
(options->userautomproc)(0,tmp_perm,NULL,0,0,n);
}
}
if (orbits)
{ for (i = 0; i < n; ++i) orbits[i] = i; if (which == 1) /* {a,b,c} */
{
min = (a < b ? a : b); min = (min < c ? min : c);
orbits[a] = orbits[b] = orbits[c] = min;
} else/* {a,b} and {c,d} */
{
orbits[a] = orbits[b] = (a < b ? a : b);
orbits[c] = orbits[d] = (c < d ? c : d);
}
}
if (options->getcanon && which == 3)
{ /* lab is already in canonical order except possibly in the
case which == 3. In that case c and d might need swapping. */
/* We know the canonicity test (if any) has been passed. However, wemightneedthegroupandtheorbits.Ifonlytheorbits,we
will make them from the cells. Otherwise we need nauty. */
if (options->userautomproc)
{
orbs = (orbits ? orbits : tmp_orbits);
active = 0; /* No need to refine further */
nauty(undirected_poset,lab,ptn,&active,orbs,options,&stats,
workspace,2,1,n,canong); returnTRUE;
}
if (orbits)
{ for (i = 0; i < n; ++i)
{
min = lab[i]; for (j = i; ptn[j] > 0; )
{
++j; if (lab[j] < min) min = lab[j];
}
/* Now i..j is the cell and min is its least vertex */ for ( ; i <= j; ++i) orbits[lab[i]] = min;
}
}
returnTRUE;
} else
{
++nauty_count5;
orbs = (orbits ? orbits : tmp_orbits);
active = 0; /* No need to refine further */
nauty(undirected_poset,lab,ptn,&active,orbs,options,&stats,
workspace,2,1,n,canong);
if (options->getcanon)
{ for (i = n-1; (bit[lab[i]]&good_vertices) == 0 ; --i) {} return orbs[lab[i]] == orbs[n-1];
}
staticvoid all_subsets(int start, int end, graph list[])
/* Computes all nonempty subsets of the set {start, .... ,end} in lexicographicallydecreasingorderandwritesthemintolist[].In factthelexicographicvalueisjusttheoppositeofthenumerical one,thatis:{0}islargerthan{1},{1,3}largerthan{2,3} largerthan{2}etc.
*/
{
graph *runlist;
runlist=list; if (end<start) { fprintf(stderr,"Error in all_subsets: end= %d start=%d\n",end, start); exit(1); }
{ fprintf(stderr,"\nUsage: %s n [o|t] [q] [m x y] where n <= 16 is the number of points\n",name );
fprintf(stderr," Generate the Hasse diagrams of the posets with n points\n");
fprintf(stderr," o causes digraph6 output in arbitrary order to be written to stdout\n");
fprintf(stderr," t causes digraph6 output in topological order to be written to stdout\n");
fprintf(stderr," q supresses statistics except for the final count\n");
fprintf(stderr," m x y with 0 <= x < y divides the generation\n" " into y parts and writes only part x.\n"); exit(0); }
/* Hier werden die Graphen konstruiert, die mit einer Kette von jeweilsnureinemKnotenproLevelanfangen.Sieentstehenaus denenmitwenigerPunkten,indemdasgesamteersteLevelmit einemzusaetzlichenPunktverbundenwirdunddanacheineKette
entsprechender Laenge angehaengt wird. */
/* Fuegt den letzten Punkt hinzu -- wie add_point, nur dass Sachen, die der Weiterfuehrung
dienen nicht mehr gemacht werden. Darf nur fuer den letzten Punkt aufgerufen werden ! */
staticvoid add_last_point (int level, int do_nauty, graph *last_nbs, graph maxpoints, int relationscounter)
/* do_nauty ist genau dann 0, wenn nauty gerade ausgefuehrt wurde, um die KanonizitaetdesletztenKnotenszutesten,orbits,generators,etc
also noch aktuell sind */
{
graph *local_nonequiv_nbs; /* hier werden die wirklich verschiedenen nbs gespeichert.
Bei trivialer Gruppe gleich nbs[level-1] */
graph *previous_nbs; int *local_lab; int *local_ptn; int nautylab[MAXN], nautyptn[MAXN];
graph *local_orbitnumber; int i, j, l, test, test2, merke; int newlevel=0;
graph dummy, dummy2, *run, *ende, *local_subsets, *local_nbs;
setword good_vertices;
boolean accept; int op_triv=0; /* Ist die Operation der Gruppe auf den Nachbarschaften leicht als
trivial zu erkennen ? */ unsignedlongint faerbung[CHECKSIZE]; /* Fuer eine heuristische Ueberpruefung, ob die Gruppe
trivial operiert */ unsignedlongint merkef;
graph which_nbs[MAXN]; int nicht_kanonisch, levelm1, anzahl_save_orbits; char *i2;
levelm1=level-1;
if (points_on_level[level]==0)
{ if (level>1) all_points_on[levelm1]=(all_points_up_to[levelm1]&(~all_points_up_to[level-2]));
/* Jetzt die Vorgaenger fuer diese neuen Mengen berechnen */ for (i=anzahl_subsets[levelm1]-1; i>=0; i--)
{ if (POPCOUNT(local_subsets[i])>1) /* die sind schon festgelegt und wuerden sogar zerstoert, wenn
sie behandelt wuerden */
{ dummy=local_subsets[i];
all_ancestors[dummy]=0;
FOREACH(i2,dummy) all_ancestors[dummy] |= all_ancestors[bit[*i2]];
}
}
/* Jetzt mit den vorigen Nachbarschaften kombinieren: */
if (level==1) {local_nbs=local_subsets;
memcpy(nbs[0],local_subsets,sizeof(graph)*anzahl_subsets[0]);
anzahl_nbs[levelm1]=anzahl_subsets[levelm1]; } else
{ local_nbs=run=nbs[levelm1]; for (i=0; (dummy=local_subsets[i])!=0; i++)
{ *run=dummy; run++;
dummy2=all_ancestors[dummy]; for (l=level-2; (l>=0) && (dummy2!=all_points_up_to[l]); l--) if ((~dummy2) & all_points_on[l]) /* since all nbs on this level contain at least onepointfromthislevel,thereshouldbeno
in dummy2 */
{ merke=first_point_level[l]; for (previous_nbs=nbs[l] ; *previous_nbs !=0; merke++ )
{ if (ISELEMENT(&dummy2,merke)) previous_nbs=jump[l][merke]; else
{
ende=jump[l][merke]; for (;*previous_nbs!=0;previous_nbs++) if ((dummy2 & *previous_nbs)==0) /* Nur wenn der Schnitt leer ist, ist es
eine moegliche Nachbarschaft */
{ *run=(dummy | *previous_nbs);
all_ancestors[*run] = dummy2 | all_ancestors[*previous_nbs];
run++; }
}
} /*ende for ueber alle vorherigen nbs */
}
}
*run=0;
anzahl_nbs[levelm1]=run-local_nbs; /* Jetzt ein paar Heuristiken, bei denen man erkennt, dass die Gruppe trivial operiert */
if ((anzahl_nbs[levelm1]==1)
|| ((anzahl_nbs[levelm1]==2) && (POPCOUNT(local_nbs[0])!=POPCOUNT(local_nbs[1]))))
{ maxorbsize_level[levelm1]=1;
op_triv=1; } else if (anzahl_nbs[levelm1] < MAXCHECK)
{ local_orbitnumber=orbitnumber[0]; for (j=anzahl_nbs[levelm1]-1; j>=0; j--)
faerbung[j]=POPCOUNT(local_nbs[j]&all_points_on[levelm1])
+(local_orbitnumber[local_nbs[j]&all_points_up_to[0]]<<1); for (i=level-2; i>0; i--)
{ local_orbitnumber=orbitnumber[i]; for (j=anzahl_nbs[levelm1]-1; j>=0; j--)
faerbung[j]+=
(local_orbitnumber[local_nbs[j]&all_points_on[i]]<<(i+1));
} /* wenn alle Farben verschieden sind operiert die Gruppe mit Sicherheit trivial */ for (j=anzahl_nbs[levelm1]-1,test=1; j>0; j--) for (i=0; i<j; i++) if (faerbung[i]==faerbung[j]) test=0; if (test)
{
maxorbsize_level[levelm1]=1;
op_triv=1; }
} /* Ende 2. Heuristik */
}
local_lab=lab[levelm1]; local_ptn=ptn[levelm1];
} /* Ende neues level */ else { local_nbs= nbs[levelm1]; /* existieren schon */ local_lab=lab[level]; local_ptn=ptn[level]; }
if ((((points_on_level[level]==0) && !op_triv) || (maxorbsize_level[levelm1]!=1)) && do_nauty)
{
number_of_generators=0; if ((points_on_level[level]==0) && (maxorbsize_level[level-2]==1)) /* fuer level==1 wird das nicht
aufgerufen, da do_nauty=0 */
{ for (i=0; i<=first_point_level[levelm1];i++) orbits[i]=i; for ( ; i<=punktzahl-1; i++)
{ if (poset[i]==poset[i-1])
{
transposition[number_of_generators++] = bit[i-1]|bit[i];
orbits[i]=orbits[i-1];
} else orbits[i]=i;
} if (number_of_generators==0) stats.numorbits=punktzahl; else stats.numorbits=0; /* Hack */
} else {
memcpy(nautylab,local_lab,sizeof(int)*punktzahl);
memcpy(nautyptn,local_ptn,sizeof(int)*punktzahl);
call_nauty(nautylab,nautyptn,punktzahl,orbits,0,&options);
}
}
if (points_on_level[level]==0)
{ newlevel=1;
first_point_level[level]=punktzahl; if (op_triv || (stats.numorbits==punktzahl)) maxorbsize_level[levelm1]=1; else maxorbsize_level[levelm1]=INT_MAX; /* erstmal */
}
/* Jetzt die wirklich verschiedenen Nachbarschaften berechnen */
maxpoints |= bit[punktzahl];
/* Nun zum Hinzufuegen des Punktes: */
if (points_on_level[level]==0)
{
if (maxorbsize_level[levelm1]==1) { local_nonequiv_nbs=local_nbs;
local_orbitnumber=orbitnumber[levelm1];
anzahl_save_orbits=anzahl_nbs[levelm1];
} else {
local_nonequiv_nbs=nonequiv_nbs[punktzahl]; /* punktzahl, bevor noch einer hinzugefuegt wird.
Hier wird nur Speicher bereitgestellt. */
anzahl_save_orbits=
compute_orbits(local_nbs,local_nonequiv_nbs,1,level,0,maxorbsize_level+levelm1);
}
anzahl_posets+=anzahl_save_orbits;
punkte_letztes_level[points_on_level[level]+1]+=anzahl_save_orbits;
anzahl_level[level]+=anzahl_save_orbits; for (run=local_nonequiv_nbs; *run!=0; run++)
{ dummy=all_ancestors[*run];
relations[relationscounter+POPCOUNT(dummy)]++;
minmaxline[POPCOUNT(maxpoints & ~dummy)]++; #ifndef COUNT if (output)
{ poset[punktzahl]=*run; punktzahl++; aufschreiben(); punktzahl--; } #endif
}
} /* ende noch kein Punkt auf dem Level */
else/* d.h. es ist mindestens ein Punkt bereits auf dem Level */
{
local_orbitnumber=orbitnumber[levelm1];
if (maxorbsize_level[levelm1]==1) { local_nonequiv_nbs=last_nbs;
i= last_nbs-nbs[levelm1];
anzahl_save_orbits=anzahl_nbs[levelm1]-i;
} else {
local_nonequiv_nbs=nonequiv_nbs[punktzahl]; /* punktzahl, bevor noch einer hinzugefuegt wird
hier wird nur Speicher bereitgestellt. */
anzahl_save_orbits=
compute_orbits(local_nbs,local_nonequiv_nbs,0,level,local_orbitnumber[poset[punktzahl-1]],0);
}
/* Jetzt wirklich den Punkt hinzufuegen */
local_lab[punktzahl]=punktzahl; local_ptn[punktzahl]=0;
local_ptn[punktzahl-1]=1; /* nauty wird hier nur gerufen, wenn die orbitzahlen der beiden
letzten gleich sind */
punktzahl++;
points_on_level[level]++;
anzahl_posets+=anzahl_save_orbits;
punkte_letztes_level[points_on_level[level]]+=anzahl_save_orbits;
anzahl_level[level]+=anzahl_save_orbits; /* die mit strikt groesserer Orbitnummer koennen schon mal gesammelt hinzugezaehlt werden */
for (run=local_nonequiv_nbs; *run!=0; run++)
{ poset[punktzahl-1]=*run;
if ((maxorbsize_level[levelm1]==1) ||
(local_orbitnumber[*run]>local_orbitnumber[poset[punktzahl-2]]))
{ dummy=all_ancestors[*run];
relations[relationscounter+POPCOUNT(dummy)]++;
minmaxline[POPCOUNT(maxpoints & ~dummy)]++; #ifndef COUNT if (output)
{ aufschreiben(); } #endif
} else/* zunaechst noch zwei Heuristiken: Wenn alle mit der gleichen orbitnummer identische Nachbarschaftenhaben(d.h.nichtverschiedene,dienurimgleichenOrbitliegen),
so ist es auch OK */
{ j=local_orbitnumber[*run]; for (i=punktzahl-2, test=1; (local_orbitnumber[poset[i]]==j)
&& (i>=first_point_level[level]);i--) if (poset[i]!=*run) test=0; if (test)
{ anzahl_posets++;
punkte_letztes_level[points_on_level[level]]++;
anzahl_level[level]++;
dummy=all_ancestors[*run];
relations[relationscounter+POPCOUNT(dummy)]++;
minmaxline[POPCOUNT(maxpoints & ~dummy)]++; #ifndef COUNT if (output)
{ aufschreiben(); } #endif
} else/* noch ein Sonderfall: Wenn die maximale Orbitgroesse 2 ist und noch kein Knoten mitNachbarneinesanderenOrbitsaufdemLevel,soistderKnotenkanonisch, derzudernbsmitmehrNachbarnaufdemLeveladjazentist.BeiGleichheit
sind die Knoten aequivalent */ if ((maxorbsize_level[levelm1]==2) &&
(local_orbitnumber[*run]==local_orbitnumber[poset[first_point_level[level]]]))
{ j=local_orbitnumber[*run];
dummy=*run; test=1; test2=0; for (i=punktzahl-2; (local_orbitnumber[poset[i]]==j) && (i>=first_point_level[level]);i--) if (poset[i]==dummy) test++; else test2++; if (test>=test2)
{ anzahl_posets++;
punkte_letztes_level[points_on_level[level]]++;
anzahl_level[level]++;
dummy=all_ancestors[*run];
relations[relationscounter+POPCOUNT(dummy)]++;
minmaxline[POPCOUNT(maxpoints & ~dummy)]++; #ifndef COUNT if (output)
{ aufschreiben(); } #endif
}
} else/* sonst muss vielleicht doch nauty gefragt werden, ob der letzte Punkt kanonisch ist */
{ /* zunaechst aber noch eine Heuristik: Den Knoten werden Farben abhaengig von ihrer Nachbarschaft zugeordnet.SiebestehenzumeinenausderAnzahlderKnotenmitihrerNachbarschaftund zumanderenausdemSchnittmitdenanderennbsderKnotendesgleichenLevels.Der
kanonische muss auf jeden Fall die gleiche Orbitnummer haben wie der letzte. */
RESETMARKS; for (i=points_on_level[level]; i>0; i--) { faerbung[i]=0; which_nbs[i]=0; }
faerbung[0]=1; which_nbs[0]=*run;
MARK(*run);
l=local_orbitnumber[*run]; for (i=punktzahl-2; (i>=first_point_level[level]) && (local_orbitnumber[poset[i]]==l); i--)
{ for (j=0; (which_nbs[j]!=0) && (which_nbs[j]!=poset[i]); j++); if (which_nbs[j]==0) { which_nbs[j]=poset[i]; faerbung[j]=1; } else faerbung[j]++;
}
test=1; merkef=faerbung[0]; nicht_kanonisch=0; for (j=1; (which_nbs[j]!=0); j++) { if (faerbung[j]==merkef) { test=0; /* evtl. nicht zu entscheiden */
MARK(which_nbs[j]); } elseif (faerbung[j]>merkef) nicht_kanonisch=1; }
if ((test==0) && (nicht_kanonisch==0)) /* dann probieren wir eine etwas kompliziertere
Faerbung */
{
test=1; for (i=punktzahl-2, dummy=0; (i>=first_point_level[level]); i--) if (UNMARKED(poset[i])) dummy|=poset[i];
RESETMARKS;
MARK(*run);
dummy2=(*run & dummy);
faerbung[0]+=(orbitnumber[0][dummy2 &(all_points_up_to[0])]); for (i=levelm1;i>0; i--)
faerbung[0]+=(orbitnumber[i][dummy2 &all_points_on[i]])<<i; /* Teilmengen der Punkte eines Levels haben immer eine Orbitnummer */
for (j=1; (which_nbs[j]!=0); j++) if (faerbung[j]==merkef)
{ dummy2=(which_nbs[j] & dummy);
faerbung[j]+=(orbitnumber[0][dummy2 &(all_points_up_to[0])]); for (i=levelm1;i>0; i--)
faerbung[j]+=
(orbitnumber[i][dummy2 &all_points_on[i]])<<i;
number_of_generators=0;
memcpy(nautylab,local_lab,sizeof(int)*punktzahl);
memcpy(nautyptn,local_ptn,sizeof(int)*punktzahl);
good_vertices = 0; for (i = first_point_level[level]; i < punktzahl; ++i) if (ISMARKED(poset[i])) ADDELEMENT(&good_vertices,i);
accept = call_nauty(nautylab,nautyptn,punktzahl,/*TMPorbits*/NULL,good_vertices,&options_final);
FOREACH(i2,*run) DELELEMENT(undirected_poset+(*i2),punktzahl-1);
undirected_poset[punktzahl-1]=0;
j=local_orbitnumber[poset[punktzahl-1]]; /* nur zum Puffern */ if (accept)
{ anzahl_posets++;
punkte_letztes_level[points_on_level[level]]++;
anzahl_level[level]++;
dummy=all_ancestors[*run];
relations[relationscounter+POPCOUNT(dummy)]++;
minmaxline[POPCOUNT(maxpoints & ~dummy)]++; #ifndef COUNT if (output)
{ aufschreiben(); } #endif
} /* ende akzeptiert mit nauty */
} /* ende halt doch nauty */ else/* konnte ohne nauty entschieden werden */ if ((test==1) && (nicht_kanonisch==0))
{ anzahl_posets++;
punkte_letztes_level[points_on_level[level]]++;
anzahl_level[level]++;
dummy=all_ancestors[*run];
relations[relationscounter+POPCOUNT(dummy)]++;
minmaxline[POPCOUNT(maxpoints & ~dummy)]++; #ifndef COUNT if (output)
{ aufschreiben(); } #endif
}
} /* else nauty... */
} /* else mit Heuristik... */
}
DELELEMENT(all_points_up_to+level,punktzahl-1);
points_on_level[level]--;
punktzahl--;
local_ptn[punktzahl-1]=0;
} /* ende mindestens ein Punkt bereits auf dem Level */
staticvoid add_point (int level, int do_nauty, graph *last_nbs, graph maxpoints, int relationscounter)
/* do_nauty ist genau dann 0, wenn nauty gerade ausgefuehrt wurde, um die KanonizitaetdesletztenKnotenszutesten,orbits,generators,etc
also noch aktuell sind */
{
graph *local_nonequiv_nbs; /* hier werden die wirklich verschiedenen nbs gespeichert.
Bei trivialer Gruppe gleich nbs[level-1] */
graph *previous_nbs; int *local_lab; int *local_ptn; int nautylab[MAXN], nautyptn[MAXN];
graph *local_orbitnumber; int i, j, l, test, test2, merke, nr; int newlevel=0;
graph dummy, dummy2, dummymax, *run, *ende, *local_subsets, *local_nbs;
setword good_vertices;
boolean accept;
int merkegenerators[MAXN][MAXN]; int merke_numgen; int merkeorbits[MAXN];
setword merketrans[MAXN]; int merkestatsnumorbits; int op_triv=0; /* Ist die Operation der Gruppe auf den Nachbarschaften leicht als
trivial zu erkennen ? */ unsignedlongint faerbung[CHECKSIZE]; /* Fuer eine heuristische Ueberpruefung, ob die Gruppe
trivial operiert */ unsignedlongint merkef;
graph which_nbs[MAXN]; int nicht_kanonisch, levelm1; char *i2;
levelm1=level-1;
if (mod && (punktzahl==splitpunktzahl)) { splitzaehler++; if (splitzaehler==rest) splitzaehler-=mod; elsereturn; }
if (points_on_level[level]==0)
{ if (level>1) all_points_on[levelm1]=(all_points_up_to[levelm1]&(~all_points_up_to[level-2]));
/* Jetzt die Vorgaenger fuer diese neuen Mengen berechnen */ for (i=anzahl_subsets[levelm1]-1; i>=0; i--)
{ if (POPCOUNT(local_subsets[i])>1) /* die sind schon festgelegt und wuerden sogar zerstoert, wenn
sie behandelt wuerden */
{ dummy=local_subsets[i];
all_ancestors[dummy]=0;
FOREACH(i2,dummy) all_ancestors[dummy] |= all_ancestors[bit[*i2]];
}
}
/* Jetzt mit den vorigen Nachbarschaften kombinieren: */
if (level==1) {local_nbs=local_subsets;
memcpy(nbs[0],local_subsets,sizeof(graph)*anzahl_subsets[0]);
anzahl_nbs[levelm1]=anzahl_subsets[levelm1]; } else
{ local_nbs=run=nbs[levelm1]; for (i=0; (dummy=local_subsets[i])!=0; i++)
{ *run=dummy; run++;
dummy2=all_ancestors[dummy]; for (l=level-2; (l>=0) && (dummy2!=all_points_up_to[l]); l--) if ((~dummy2) & all_points_on[l])
{ merke=first_point_level[l]; for (previous_nbs=nbs[l] ; *previous_nbs !=0; merke++ )
{ if (ISELEMENT(&dummy2,merke)) previous_nbs=jump[l][merke]; else
{
ende=jump[l][merke]; for (;*previous_nbs!=0;previous_nbs++) if ((dummy2 & *previous_nbs)==0) /* Nur wenn der Schnitt leer ist, ist es
eine moegliche Nachbarschaft */
{ *run=(dummy | *previous_nbs);
all_ancestors[*run] = dummy2 | all_ancestors[*previous_nbs];
run++; }
}
} /*ende for ueber alle vorherigen nbs */
}
}
*run=0;
anzahl_nbs[levelm1]=run-local_nbs;
/* Jetzt ein paar Heuristiken, bei denen man sofort erkennt, dass die Gruppe trivial operiert */
if ((anzahl_nbs[levelm1]==1)
|| ((anzahl_nbs[levelm1]==2) && (POPCOUNT(local_nbs[0])!=POPCOUNT(local_nbs[1]))))
{ maxorbsize_level[levelm1]=1;
op_triv=1; } else if (anzahl_nbs[levelm1] < MAXCHECK)
{ local_orbitnumber=orbitnumber[0]; for (j=anzahl_nbs[levelm1]-1; j>=0; j--)
faerbung[j]=POPCOUNT(local_nbs[j]&all_points_on[levelm1])
+(local_orbitnumber[local_nbs[j]&all_points_up_to[0]]<<1); for (i=level-2; i>0; i--)
{ local_orbitnumber=orbitnumber[i]; for (j=anzahl_nbs[levelm1]-1; j>=0; j--)
faerbung[j]+=
(local_orbitnumber[local_nbs[j]&all_points_on[i]]<<(i+1));
} /* wenn alle Farben verschieden sind operiert die Gruppe mit Sicherheit trivial */ for (j=anzahl_nbs[levelm1]-1,test=1; j>0; j--) for (i=0; i<j; i++) if (faerbung[i]==faerbung[j]) test=0; if (test)
{
maxorbsize_level[levelm1]=1;
op_triv=1; }
} /* Ende 2. Heuristik */
} /* ende level>1 */
if (punktzahl<=maxpunktzahl-2) for (i=first_point_level[levelm1], run=local_nbs; i<punktzahl; i++)
{ while (ISELEMENT(run,i)) run++;
jump[levelm1][i]=run; }
local_lab=lab[levelm1]; local_ptn=ptn[levelm1];
} /* Ende neues level */ else { local_nbs= nbs[levelm1]; /* existieren schon */ local_lab=lab[level]; local_ptn=ptn[level]; }
if ((((points_on_level[level]==0) && !op_triv) || (maxorbsize_level[levelm1]!=1)) && do_nauty)
{
number_of_generators=0; if ((points_on_level[level]==0) && (maxorbsize_level[level-2]==1)) /* fuer level==1 wird das nicht
aufgerufen, da do_nauty=0 */
{ for (i=0; i<=first_point_level[levelm1];i++) orbits[i]=i; for ( ; i<=punktzahl-1; i++)
{ if (poset[i]==poset[i-1])
{
transposition[number_of_generators++] = bit[i-1]|bit[i];
orbits[i]=orbits[i-1];
} else orbits[i]=i;
} if (number_of_generators==0) stats.numorbits=punktzahl; else stats.numorbits=0; /* Hack */
} else {
memcpy(nautylab,local_lab,sizeof(int)*punktzahl);
memcpy(nautyptn,local_ptn,sizeof(int)*punktzahl);
call_nauty(nautylab,nautyptn,punktzahl,orbits,0,&options);
}
}
if (points_on_level[level]==0) /* ein neues level -- lab kann neu festgelegt werden */
{ newlevel=1;
first_point_level[level]=punktzahl;
local_lab=lab[level]; local_ptn=ptn[level]; /* zunaechst lab: */ if (op_triv || (stats.numorbits==punktzahl))
{ maxorbsize_level[levelm1]=1; for (i=0; i<punktzahl; i++)
{ local_lab[i]=i; local_ptn[i]=0;}
} else
{ maxorbsize_level[levelm1]=INT_MAX; /* erstmal */ for (i=0; i<punktzahl; i++) local_lab[i]=i;
qsort((void *)local_lab, punktzahl, sizeof(int), compare_sets);
local_ptn[punktzahl-1]=0; for (i=punktzahl-2; i>=0; i--)
{ if (orbits[local_lab[i+1]] == orbits[local_lab[i]]) local_ptn[i]=1; else local_ptn[i]=0;
}
}
}
/* Jetzt die wirklich verschiedenen Nachbarschaften berechnen */
/* Nun zum Hinzufuegen des Punktes: */
maxpoints |= bit[punktzahl];
if (points_on_level[level]==0)
{
if (maxorbsize_level[levelm1]==1) { local_nonequiv_nbs=local_nbs;
local_orbitnumber=orbitnumber[levelm1]; for (i=1, run=local_nonequiv_nbs; *run != 0; run++, i++)
local_orbitnumber[*run]=i;
} else {
local_nonequiv_nbs=nonequiv_nbs[punktzahl]; /* punktzahl, bevor noch einer hinzugefuegt wird.
Hier wird nur Speicher bereitgestellt. */
compute_orbits(local_nbs,local_nonequiv_nbs,1,level,0,maxorbsize_level+levelm1); if (maxorbsize_level[levelm1]==1)
{ for (i=0; i<punktzahl; i++) local_ptn[i]=0; local_nonequiv_nbs=local_nbs;} /* SATZ: Der so gefaerbte Graph hat die gleichen Orbits auf den nbs auch fuer kommende
Level und liefert ein korrektes Kanonizitaetskriterium */
FOREACH(i2,*run) DELELEMENT(undirected_poset+(*i2),punktzahl-1);
}
poset[punktzahl-1]=undirected_poset[punktzahl-1]=0;
DELELEMENT(all_points_up_to+level,punktzahl-1);
points_on_level[level]--;
punktzahl--;
} /* ende noch kein Punkt auf dem Level */
else/* d.h. es ist mindestens ein Punkt bereits auf dem Level */
{
local_orbitnumber=orbitnumber[levelm1];
if (maxorbsize_level[levelm1]==1) local_nonequiv_nbs=last_nbs; else {
local_nonequiv_nbs=nonequiv_nbs[punktzahl]; /* punktzahl, bevor noch einer hinzugefuegt wird
hier wird nur Speicher bereitgestellt. */
compute_orbits(local_nbs,local_nonequiv_nbs,0,level,local_orbitnumber[poset[punktzahl-1]],0);
}
/* Jetzt wirklich den Punkt hinzufuegen */
local_lab[punktzahl]=punktzahl; local_ptn[punktzahl]=0;
punktzahl++;
points_on_level[level]++;
ADDELEMENT(all_points_up_to+level,punktzahl-1); for (run=local_nonequiv_nbs; *run!=0; run++)
{ poset[punktzahl-1]=undirected_poset[punktzahl-1]=*run;
FOREACH(i2,*run) ADDELEMENT(undirected_poset+(*i2),punktzahl-1); if (local_orbitnumber[poset[punktzahl-2]]==local_orbitnumber[*run])
local_ptn[punktzahl-2]=1; else local_ptn[punktzahl-2]=0;
if ((maxorbsize_level[levelm1]==1) ||
(local_orbitnumber[*run]>local_orbitnumber[poset[punktzahl-2]]))
{
aufschreiben_2(level,dummymax,nr); if (punktzahl==(maxpunktzahl-1)) { add_last_point(level,1,run,dummymax,nr);
add_last_point(level+1,1,run,dummymax,nr); } else { add_point(level,1,run,dummymax,nr);
add_point(level+1,1,run,dummymax,nr); }
} else/* zunaechst noch zwei Heuristiken: Wenn alle mit der gleichen orbitnummer identische Nachbarschaftenhaben(d.h.nichtverschiedene,dienurimgleichenOrbitliegen),
so ist es auch OK */
{ j=local_orbitnumber[*run]; for (i=punktzahl-2, test=1; (local_orbitnumber[poset[i]]==j)
&& (i>=first_point_level[level]);i--) if (poset[i]!=*run) test=0; if (test)
{
aufschreiben_2(level,dummymax,nr); if (punktzahl==(maxpunktzahl-1)) { add_last_point(level,1,run,dummymax,nr);
add_last_point(level+1,1,run,dummymax,nr); } else { add_point(level,1,run,dummymax,nr);
add_point(level+1,1,run,dummymax,nr); }
} else/* noch ein Sonderfall: Wenn die maximale Orbitgroesse 2 ist und noch kein Knoten mitNachbarneinesanderenOrbitsaufdemLevel,soistderKnotenkanonisch, derzudernbsmitmehrNachbarnaufdemLeveladjazentist.BeiGleichheit,
sind die Knoten aequivalent */ if ((maxorbsize_level[levelm1]==2) &&
(local_orbitnumber[*run]==local_orbitnumber[poset[first_point_level[level]]]))
{ j=local_orbitnumber[*run];
dummy=*run; test=1; test2=0; for (i=punktzahl-2; (local_orbitnumber[poset[i]]==j) && (i>=first_point_level[level]);i--) if (poset[i]==dummy) test++; else test2++; if (test>=test2)
{
aufschreiben_2(level,dummymax,nr); if (punktzahl==(maxpunktzahl-1)) { add_last_point(level,1,run,dummymax,nr);
add_last_point(level+1,1,run,dummymax,nr); } else { add_point(level,1,run,dummymax,nr);
add_point(level+1,1,run,dummymax,nr); }
}
} else/* sonst muss vielleicht doch nauty gefragt werden, ob der letzte Punkt kanonisch ist */
{ /* zunaechst aber noch eine Heuristik: Den Knoten werden Farben abhaengig von ihrer Nachbarschaft zugeordnet.SiebestehenzumeinenausderAnzahlderKnotenmitihrerNachbarschaftund zumanderenausdemSchnittmitdenanderennbsderKnotendesgleichenLevels.Der
kanonische muss auf jeden Fall die gleiche Orbitnummer haben wie der letzte. */
RESETMARKS; for (i=points_on_level[level]; i>0; i--) { faerbung[i]=0; which_nbs[i]=0; }
faerbung[0]=1; which_nbs[0]=*run;
MARK(*run);
l=local_orbitnumber[*run]; for (i=punktzahl-2; (i>=first_point_level[level]) && (local_orbitnumber[poset[i]]==l); i--)
{ for (j=0; (which_nbs[j]!=0) && (which_nbs[j]!=poset[i]); j++); if (which_nbs[j]==0) { which_nbs[j]=poset[i]; faerbung[j]=1; } else faerbung[j]++;
}
test=1; merkef=faerbung[0]; nicht_kanonisch=0; for (j=1; (which_nbs[j]!=0); j++) { if (faerbung[j]==merkef) { test=0; /* evtl. nicht zu entscheiden */
MARK(which_nbs[j]); } elseif (faerbung[j]>merkef) nicht_kanonisch=1; }
if ((test==0) && (nicht_kanonisch==0)) /* dann probieren wir eine etwas kompliziertere
Faerbung */
{
test=1; for (i=punktzahl-2, dummy=0; (i>=first_point_level[level]); i--) if (UNMARKED(poset[i])) dummy|=poset[i];
RESETMARKS;
MARK(*run);
dummy2=(*run & dummy);
faerbung[0]+=(orbitnumber[0][dummy2 &(all_points_up_to[0])]); for (i=levelm1;i>0; i--)
faerbung[0]+=(orbitnumber[i][dummy2 &all_points_on[i]])<<i; /* Teilmengen der Punkte eines Levels haben immer eine Orbitnummer */
for (j=1; (which_nbs[j]!=0); j++) if (faerbung[j]==merkef)
{ dummy2=(which_nbs[j] & dummy);
faerbung[j]+=(orbitnumber[0][dummy2 &(all_points_up_to[0])]); for (i=levelm1;i>0; i--)
faerbung[j]+=
(orbitnumber[i][dummy2 &all_points_on[i]])<<i;
if ((test==0) && (nicht_kanonisch==0)) /* OK -- dann halt doch nauty... */
{
number_of_generators=0;
memcpy(nautylab,local_lab,sizeof(int)*punktzahl);
memcpy(nautyptn,local_ptn,sizeof(int)*punktzahl);
good_vertices = 0; for (i = first_point_level[level]; i < punktzahl; ++i) if (ISMARKED(poset[i])) ADDELEMENT(&good_vertices,i);
accept = call_nauty(nautylab,nautyptn,punktzahl,orbits,good_vertices,&options_canon);
j=local_orbitnumber[poset[punktzahl-1]]; /* nur zum Puffern */ if (accept)
{
aufschreiben_2(level,dummymax,nr); if (punktzahl==(maxpunktzahl-1))
{ add_last_point(level+1,0,run,dummymax,nr); /* hier wird nauty garantiert nicht noch einmal aufgerufen,deshalbmussmansichdieSachen
nicht merken */
add_last_point(level,0,run,dummymax,nr); } else
{
merkestatsnumorbits=stats.numorbits;
merke_numgen=number_of_generators;
memcpy(merkeorbits,orbits,sizeof(int)*punktzahl);
memcpy(merkegenerators,generators,sizeof(int)*number_of_generators*MAXN);
memcpy(merketrans,transposition,sizeof(setword)*number_of_generators);
add_point(level+1,0,run,dummymax,nr);
}
} /* ende akzeptiert mit nauty */
} /* ende halt doch nauty */ else/* konnte ohne nauty entschieden werden */ if ((test==1) && (nicht_kanonisch==0))
{
aufschreiben_2(level,dummymax,nr); if (punktzahl==(maxpunktzahl-1)) { add_last_point(level,1,run,dummymax,nr);
add_last_point(level+1,1,run,dummymax,nr); } else { add_point(level,1,run,dummymax,nr); add_point(level+1,1,run,dummymax,nr); }
}
} /* else nauty... */
} /* else mit Heuristik... */
FOREACH(i2,*run) DELELEMENT(undirected_poset+(*i2),punktzahl-1);
}
poset[punktzahl-1]=undirected_poset[punktzahl-1]=0;
DELELEMENT(all_points_up_to+level,punktzahl-1);
points_on_level[level]--;
punktzahl--;
local_ptn[punktzahl-1]=0;
} /* ende mindestens ein Punkt bereits auf dem Level */
{ int i,j,k,quiet,pruned;
graph dummy; double time0,time1;
nauty_check(WORDSIZE,1,1,NAUTYVERSIONID);
if (argc==1) usage(argv[0]); if (argc == 2 && (strcmp(argv[1],"-help") == 0 || strcmp(argv[1],"--help") == 0))
usage(argv[0]);
if (!isdigit(argv[1][0])) usage(argv[0]);
quiet = topsort = 0; for (i=2;i<argc;i++)
{ if (argv[i][0]=='o') { output=1; #ifdef COUNT
fprintf(stderr,"The program was compiled with -DCOUNT, so it cannot output posets !\n"); exit(1); #endif
} else if (argv[i][0]=='t') { output=1; topsort=1; #ifdef COUNT
fprintf(stderr,"The program was compiled with -DCOUNT, so it cannot output posets !\n"); exit(1); #endif
} else if (argv[i][0]=='q') quiet = 1; else if ((argv[i][0]=='m') && isdigit(argv[i+1][0]) && isdigit(argv[i+2][0]))
{ i++; rest=atoi(argv[i]); i++; mod=atoi(argv[i]); } else usage(argv[0]); }
if (mod && ((rest<0) || (rest>=mod))) usage(argv[0]);
if (sizeof(unsignedlongint)<4) { fprintf(stderr,">E sizeof(unsigned long int) should be at least 4\n"); exit(1); }
if (!quiet) { for (i=0;i<argc;i++) fprintf(stderr,"%s ",argv[i]); fprintf(stderr,"\n"); }
time0 = CPUTIME;
for (i=0;i<MAXN;i++) orbitnumber[i][0]=0;
#if0// BDM for (i=0;i<=MAXGRAPH;i++)
{ dummy=i;
first_bit[i]=FIRSTBIT(dummy); for (j=0;j<MAXN;j++)
{ if ((k=nextelement(&dummy,1,j))>=0) next_element[i][j]=k; else next_element[i][j]=MAXN; if ((k=nextelement(&dummy,1,-1))>=0) next_element[i][MAXN]=k; else next_element[i][MAXN]=MAXN;
} for (j=MAXN, k=0; (j=next_element[i][j])!=MAXN; ) { elementlist[i][k]=j; k++; }
elementlist[i][k]=MAXN;
} #else for (i=0;i<=MAXGRAPH;i++)
{
dummy = i;
k = 0; while (dummy)
{ TAKEBIT(elementlist[i][k],dummy);
k++;
}
elementlist[i][k]=MAXN;
} #endif
maxpunktzahl=atoi(argv[1]);
if (maxpunktzahl>MAXN) { fprintf(stderr,">E No more than %d points are supported.\n",MAXN);
fprintf(stderr,"Note that changing MAXN is a nontrivial task.\n"); exit(1); }
if (mod && (maxpunktzahl<6)) { fprintf(stderr,">E Need at least 6 vertices for splitting\n"); exit(1); } if (mod) { splitpunktzahl=maxpunktzahl-4; if (splitpunktzahl>11) splitpunktzahl=11; }
init_nauty();
first_point_level[0]=0; for (points_on_level[0]=2; points_on_level[0]<maxpunktzahl; points_on_level[0]++) if ((mod==0) || (rest==0) || (points_on_level[0]<=splitpunktzahl))
{ punktzahl=points_on_level[0];
maxorbsize_level[0]=punktzahl;
all_points_up_to[0]=0; for (i=0; i<punktzahl; i++) { lab[0][i]=i; ptn[0][i]=1; all_ancestors[bit[i]]=bit[i];
ADDELEMENT(all_points_up_to,i); orbits[i]=1;}
all_points_on[0]=all_points_up_to[0];
ptn[0][punktzahl-1]=0;
number_of_generators=0; for (i=0; i<punktzahl-1; i++)
{
minmaxline=minmax_counter[punktzahl];
aufschreiben_2(0,all_points_on[0],0); if (punktzahl==(maxpunktzahl-1)) add_last_point(1,0,0,all_points_on[0],0); else add_point(1,0,0,all_points_on[0],0);
} /* Die Faelle nur ein Level bzw ueberall nur ein Punkt extra: */ if (rest==0) /* d.h. auch wenn garnicht gesplittet wird */
{ if (maxpunktzahl>1) aufschreiben_3();
if (!quiet)
{
fprintf(stderr,"\nPosets with 1 point on the last level: %lld\n",punkte_letztes_level[1]); for (i=2; i<=maxpunktzahl; i++) fprintf(stderr,"Posets with %2d points on the last level: %lld\n",i,punkte_letztes_level[i]);
fprintf(stderr,"\n");
fprintf(stderr,"Posets with 1 level: %lld\n",anzahl_level[0]); for (i=2; i<=maxpunktzahl; i++) fprintf(stderr,"Posets with %2d levels: %lld\n",i,anzahl_level[i-1]);
fprintf(stderr,"nauty counts: %lld %lld %lld %lld %lld\n",nauty_count1,nauty_count2,nauty_count3,nauty_count4,nauty_count5);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.