// compile: cc -O4 -o ucs ucs.c
// start without parameters or with parameter h to get usage information

// to use the processor built in popcount where available change #define and 
// use compiler option -mpopcnt or -msse4.2

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<ctype.h>
#include<limits.h>
#include <time.h>
#include <sys/times.h>
#include <string.h>
//#include "nauty.h"

#define N 8    /* uses unsigned short -- so don't increase above 16 -- for the definition of
                              permutation don't increase above 15.  */

#if N>8
#define USE_USHORT 1
#else
#define USE_USHORT 0
#endif
#define USE_UCHAR (!USE_USHORT)

#if USE_USHORT
typedef unsigned short settype;
#else
typedef unsigned char settype;
#endif

#ifdef TEST
#define STATS
#endif 

#define BIT(i) (((settype)1)<<(i-1))
#define DELBIT(a,i) ((a)&=(~BIT(i)))
#define SETBIT(a,i) ((a)|=(BIT(i)))

int set_is_present[1<<(N)] ={0}; // is a bit faster with int instead of char :-(

#if USE_USHORT
#define FIVE_MASK_VALUE (32)
#define FIVE_MASK (31)
#define FIRST5(x) ((x)&FIVE_MASK)
#define SECOND5(x) ((x>>5)&FIVE_MASK)
#define THIRD5(x) ((x>>10)&FIVE_MASK)
typedef settype  image[3]; 
typedef image permutation[FIVE_MASK_VALUE];
/* the_permutations[x][FIRST5(i)][0] will be the image of permutation number x of the first 5 bytes of the set i,
    the_permutations[x][SECOND5(i)][1] of the second 5, etc  */
#define IMAGE(number,set) (the_permutations[number][FIRST5(set)][0] | the_permutations[number][SECOND5(set)][1]\
			   | the_permutations[number][THIRD5(set)][2])
#else
typedef settype permutation[256];
/* the_permutations[x][y] will be the image of permutation number x of the set y */
#define IMAGE(number,set) (the_permutations[number][set])
#endif


permutation *the_permutations; 
int number_permutations=0;

unsigned int _gbu;
#define FORALLELEMENTS(x,y) for (_gbu=x, y= __builtin_ffs(_gbu); y; _gbu&=(_gbu-1), y= __builtin_ffs(_gbu))

#define LESS_THAN_2BIT(a) (((a)&((a)-1))==0)

settype all_subsets[N+1][1<<(N)];
int number_all_subsets[N+1];

int n;
settype ucs[1<<(N)];

int number_sets, start_level[N+2];
int permutation_number_level[N+1][6000]; // restricted to at most 6000 permutations -- decrease later
int number_auts[N+1];

long int structurecounter=0L;
unsigned long long int labelled_structurecounter=0ULL; 
int nfak; 
// just large enough for the labelled UCS on 7 elements -- if done in one run... :-) 
int nfak; 

#ifdef STATS
long int not_uc=0L, well_uc=0L, structurecounter_numbersets[1<<N]={0L},  structurecounter_numbersets_sparse[1<<N]={0L}, number_sparse=0L;
int weight=0;
#endif

#define POPC(x) (popfunction(x))
//#define POPC(x) (__builtin_popcount(x)) 
//#define POPC(x) POPCOUNT(x) /* needs nauty.h */

//int teller[6]={0}, teller2[6]={0};

unsigned int _mark_sets=UINT_MAX;
unsigned int _marks_sets[1<<N];
#define RESETMARKS_SETS {int _i; if (_mark_sets==UINT_MAX) \
				   { _mark_sets=1; for(_i=0;_i<(1<<N);_i++) _marks_sets[_i]=0U; } else _mark_sets++;}
#define MARK_SET(x) (_marks_sets[x]=_mark_sets)
#define UNMARKED(x) (_marks_sets[x]!=_mark_sets)

int rest=0, mod=0, modcounter, maxsets=INT_MAX, splitlevel=0, writeout=0, writebinary=0, minlevel=1;
int rest2=0, mod2=0, splitlevel2=0, modcounter2;
//long int splitcounter;

void write_ucs();

int popfunction(unsigned int x)
{
  int i;

  for (i=0; x; i++) x &= x-1;
  return i;
}

#ifdef TEST
void test_uc()
{
  int i,j,search;
  settype the_union;

  for (i=0;i<(number_sets-1);i++)
    for (j=i+1;j<number_sets;j++)
      { the_union= ucs[i] | ucs[j];
	for (search=0; (search<i) && (ucs[search] != the_union); search++);
	if ((search==i) && ucs[i] != the_union)
	  { fprintf(stderr,"Found ucs that is not union closed!\n"); write_ucs(); exit(0); }
      }
  return;    
}
#endif

void usage(char name[])
{
  fprintf(stderr,"Usage: %s n [m res mod] [m2 res2 mod2] [s x] [w] [wb] [e y] with n>0 the number of elements in the whole set.\n",name);
  fprintf(stderr,"m res mod makes that only part number res of mod parts is generated with 0<=res<mod\n");
  fprintf(stderr,"m2 may only be used in combination with m. It splits the part given by res and mod further. 0<=res2<mod2\n");
  fprintf(stderr,"m2 should not be used for n<=6.\n");
  fprintf(stderr,"s x restricts the generation to union closed sets with at most x sets\n");
  fprintf(stderr,"w makes the program write a human readable version of each UCS to stdout.\n");
#if USE_USHORT
  fprintf(stderr,"wb makes the program write a binary code. The sets are  unsigned shorts separated by \n");
  fprintf(stderr,"unsigned shorts with value 0.\n");
#else
  fprintf(stderr,"wb makes the program write a binary code. The sets are unsigned chars separated by \n");
  fprintf(stderr,"unsigned chars with value 0.\n");
#endif
  fprintf(stderr,"e y only generates UCS where each set has a size of at least 1<=y<=n. \n");
  exit(0);
}

void writeset(settype set)
{
  int i;
  fprintf(stderr,"(");
  FORALLELEMENTS(set,i) fprintf(stderr,"%d ",i);
  fprintf(stderr,")\n");
}

void write_ucs()
{ int i,j, level, buffer;
  unsigned int s;

  level=n-1;

   //fprintf(stdout,"The union closed set (%d elements):\n",number_sets);
for (j=0;j<number_sets;j++)
  {
    if (j && (j==start_level[level])) 
      { fprintf(stdout,"X"); 
	buffer=start_level[level];
	while ((level>0) && (buffer==start_level[level])) level--;
      }
 
  fprintf(stdout,"(");
  s=ucs[j];
  fprintf(stdout,"%d", __builtin_ffs(s));
  s&=s-1;
  FORALLELEMENTS(s,i) fprintf(stdout," %d",i);
  fprintf(stdout,")");
  }
 fprintf(stdout,"\n");
}

void make_all_subsets()
{
  unsigned int set;
  int i, size;

  for (i=0;i<=n;i++) number_all_subsets[i]=0;

  for (set=0; set<(1U<<n); set++)
    {
      size=POPC(set);
      all_subsets[size][number_all_subsets[size]]=(settype)set;
      number_all_subsets[size]++;
    }

  for (i=0;i<=n;i++)
    all_subsets[i][number_all_subsets[i]]=(settype)0;
  return;
}

void go_on_permut(int imageof, int image[],int rest2[])
{
  int i, rest[N+2], el;
  settype buffer0;
#if USE_USHORT
settype buffer1,buffer2;
#endif

  if (imageof==n)
    {image[imageof]=rest2[imageof]; 
      //for (i=1;i<=n;i++) fprintf(stderr,"(%d->%d)",i,image[i]);
      //fprintf(stderr,"\n");

	if (number_permutations>=0) // the first one is the identity -- that never produces smaller labellings
	                                                // start with -1
#if USE_USHORT
	  for (i=0;i< FIVE_MASK_VALUE;i++)
	  {
	      buffer0=buffer1=buffer2=0;
	      FORALLELEMENTS(i,el) 
		{//fprintf(stderr,"el=%d\n",el); 
		  buffer0 |=BIT(image[el]);
		  buffer1 |=BIT(image[el+5]);
		  buffer2 |=BIT(image[el]+10);
		}
	      //writeset(buffer0);
	      the_permutations[number_permutations][i][0]=buffer0;
	      the_permutations[number_permutations][i][1]=buffer1;
	      the_permutations[number_permutations][i][2]=buffer2;
	    }
#else
	for (i=0; i<256; i++)
	  { buffer0=0; 
	     FORALLELEMENTS(i,el) buffer0 |=BIT(image[el]);
	     the_permutations[number_permutations][i]=buffer0;
	  }
#endif
      number_permutations++;
      return;
    }

  for (i=imageof;i<=n;i++) rest[i]=rest2[i];

  for (i=imageof;i<=n;i++)
    { image[imageof]=rest[i];
      rest[i]=rest2[i-1];
      go_on_permut(imageof+1,image,rest);
    }
  return;

}

void makepermutations(int n) // makes them in lexicographic order
{
  int i, total;
  int image[N+1], rest[N+1]; 


  for (i=2,total=1;i<=n;i++) total*=i;
  nfak=total;
  total -= 1;
  for (i=0; i<total;i++) permutation_number_level[n][i]=i;
  permutation_number_level[n][total]=0;
  number_auts[n]=total;

  the_permutations= (permutation *)malloc(total *sizeof(permutation));

  if (the_permutations==NULL) 
    { fprintf(stderr,"Don't get space for %d permutations (%ld bytes)-- exiting!\n",
	      total,(long int)total *(long int)sizeof(permutation)); exit(1); }

  for (i=1;i<=n;i++) rest[i]=i;
  rest[n+1]=0;

  number_permutations= -1; // the first one isn't written -- the identity

  for (i=1;i<=n;i++)
    { image[1]=i;
      rest[i]=i-1;
      go_on_permut(2,image,rest);
    }

  return;

}

void output(int grpsize)
{
#ifdef TEST
  int i,w;
  test_uc();
  for (i=w=0;i<number_sets;i++) w+= POPC((unsigned int)ucs[i]);
  if (w!=weight) { fprintf(stderr,"Problem with computation of weights! \n"); exit(1); }
#endif

  structurecounter++;
  labelled_structurecounter += nfak/grpsize;
  if (writeout) 
    { if (writebinary)
	{ ucs[number_sets]=0;
	  if (fwrite(ucs, sizeof(settype), number_sets+1, stdout)!=number_sets+1)
	    {fprintf(stderr,"Can't write output -- fwrite() failed! Exiting.\n"); exit(1); }
	}
      else write_ucs();
    }
#ifdef STATS
  if ((weight*2)<=(number_sets*n)) 
    { number_sparse++;
       structurecounter_numbersets_sparse[number_sets]++;
    }
  structurecounter_numbersets[number_sets]++;
#endif

}

int final_is_uc(settype old_base_sets[], int number_old_base, settype new_set)
{
  int i;

  for (i=number_old_base-1; i>=0; i--)
    { if (!set_is_present[old_base_sets[i] | new_set]) return 0; }

  old_base_sets[number_old_base]=new_set;
  return 1;
  }

int is_uc(settype old_base_sets[], int number_old_base, settype new_base_sets[],
	  int *number_new_base_sets, settype new_set)
// it is assumed that new_set will be a base set (e.g. smallest size) in case the result is a UCS
{
  int i, newc;
  settype x;

  /*fprintf(stderr,"---------------------------------------------------------------------\n Testing: "); write_ucs();
  fprintf(stderr,"old base sets: \n"); for (i=0;i<number_old_base;i++) writeset(old_base_sets[i]);
  fprintf(stderr,"------------------------------------------------------------------\n");*/

  RESETMARKS_SETS;
  for (i=number_old_base-1;i>=0;i--) // small sets have a better chance to lead to contradictions (hope)
    { x= old_base_sets[i] | new_set;
      //fprintf(stderr,"Checking set (present: %d): ",set_is_present[x]); writeset(x);
      if (!set_is_present[x]) 
	{
#ifdef TEST
                    not_uc++; 
#endif
           return 0;
	}
      // else
      MARK_SET(x); 
    }

#ifdef TEST
  well_uc++;
#endif

  for (i=newc=0;i<number_old_base;i++)
    if (UNMARKED(old_base_sets[i])) { new_base_sets[newc]=old_base_sets[i]; newc++; }
  new_base_sets[newc]=new_set;
  *number_new_base_sets=newc+1;

  return 1;
}

int compare_sets(const void *a, const void *b)
{ 
  return (int)(*((settype *)a)) - (int)(*((settype *)b));
}

int minimal(level)
    /* checks whether the sets on level "level" can be mapped onto a smaller sequence of sets
       by the automorphisms on level "level+1".
       "level" must be the last level in the UCS.

       The new automorphisms are written in permutation_number_level[level]

 */
  { int i, j, autlevel, auts,perm,comparelength, start;
    //settype min; // the smallest set
    settype imagebuffer[1<<(N)], *ucsp;

    if (number_auts[level+1]==0) // only nontrivial permutations are counted
      { number_auts[level]=0; return 1; }

    for (autlevel=level+1; number_auts[autlevel]== -1; autlevel++);

    start=start_level[level];
    ucsp=ucs+start;
    //min=ucs[start];
    comparelength=number_sets-start;

    number_auts[level]=0;
if (comparelength>1)
  {
    for (i=0, auts=number_auts[autlevel]; i<auts; i++) // apply permutation
      { perm=permutation_number_level[autlevel][i];
	for (j=0;j<comparelength;j++) imagebuffer[j]= IMAGE(perm,ucsp[j]);
	qsort((void *)imagebuffer, comparelength,sizeof(settype), compare_sets);
	for (j=0;(j<comparelength) && (imagebuffer[j]==ucsp[j]);j++); 
	if (j==comparelength) { permutation_number_level[level][number_auts[level]]=perm; 
	  number_auts[level]++;
	}
	else 
	  if (imagebuffer[j]<ucsp[j]) { return 0; }
      }
  }
 else 
  {
    for (i=0, auts=number_auts[autlevel]; i<auts; i++) // apply permutation
      { perm=permutation_number_level[autlevel][i];
	imagebuffer[0]= IMAGE(perm,ucsp[0]);
	if (imagebuffer[0]==ucsp[0]) 
	  { permutation_number_level[level][number_auts[level]]=perm; 
	    number_auts[level]++;
	  }
	else 
	  if (imagebuffer[0]<ucsp[0]) { return 0; }
      }
  }


    return number_auts[level]+1;
  }

void add_next_set_2( int level, int next_to_add, settype old_base_sets[], int number_old_base )
{
 settype new_base_sets[1<<(N)], buffer;
 int number_new_base_sets, whichset, grpsize;

  if (next_to_add==0) 
   { start_level[level]=number_sets;
     // first leave out level
     if (level>minlevel) 
       {
	 if (number_auts[level+1]==0) number_auts[level]=0;
	 else number_auts[level]= -1; // as a sign to look one level higher
	 add_next_set_2( level-1, 0, old_base_sets, number_old_base);
       }
   }

 // then add the next set on level "level"

 number_sets++;
#ifdef STATS
  weight+=level;
#endif

  if (level>minlevel)
    {
      for (whichset=next_to_add; whichset <number_all_subsets[level]; whichset++)
	{
	  buffer=all_subsets[level][whichset]; 
	  if (is_uc(old_base_sets,number_old_base,new_base_sets,&number_new_base_sets,buffer))
	    {
	 ucs[number_sets-1]=buffer; 
	 set_is_present[buffer]=1;
	 if (grpsize=minimal(level))
	   { output(grpsize); 
	     if (number_sets<maxsets)
	       {
		 if (level>minlevel)  add_next_set_2( level-1, 0, new_base_sets,number_new_base_sets); 
		 /* this must come first, because otherwise the automorphisms for this level are overwritten */
		 if (whichset <number_all_subsets[level]-1)
		   add_next_set_2( level, whichset+1, new_base_sets,number_new_base_sets);
	       }
	   }
	 //ucs[number_sets-1]=0; 
	 set_is_present[buffer]=0; 
	    }
	}
    }
  else //level == minlevel maybe not worth updating base sets...
   {
      for (whichset=next_to_add; whichset <number_all_subsets[level]; whichset++)
	{
	  buffer=all_subsets[level][whichset]; 
	  if (final_is_uc(old_base_sets,number_old_base,buffer))
	    {
	      ucs[number_sets-1]=buffer; 
	      //set_is_present[buffer]=1;
	      if (grpsize=minimal(level))
		{ output(grpsize); 
		  if (number_sets<maxsets)
	         {
		   //if (level>minlevel)  add_next_set_2( level-1, 0, new_base_sets,number_new_base_sets); 
		   /* this must come first, because otherwise the automorphisms for this level are overwritten */
		   if (whichset <number_all_subsets[level]-1)
		     add_next_set_2( level, whichset+1, old_base_sets,number_old_base+1);
		 }
		}
	      //ucs[number_sets-1]=0; 
	      //set_is_present[buffer]=0; 
	    }
	}
   }


number_sets--;
#ifdef STATS
  weight-=level;
#endif

 if (next_to_add==0)  start_level[level]= -1;

 return;
}

void add_next_set_1( int level, int next_to_add, settype old_base_sets[], int number_old_base )
// the same as add_next_set() but for splitting according to splitlevel2. This function is not
// called if m2 isn't used
{
 settype new_base_sets[1<<(N)], buffer;
 int number_new_base_sets, whichset, is_splitlevel, grpsize;

  //write_ucs();
 
is_splitlevel=((number_sets - (level*3))>=splitlevel2); // can increase by 4 in one step, so can be >

 if (/*mod2 &&*/ is_splitlevel) 
   {
       if (modcounter2==mod2) modcounter2=1;
      else { modcounter2++; return; } 
   }

  if (next_to_add==0) 
   { start_level[level]=number_sets;
     // first leave out level
     if (level>minlevel) 
       {
	 if (number_auts[level+1]==0) number_auts[level]=0;
	 else number_auts[level]= -1; // as a sign to look one level higher
	 if (is_splitlevel) 
	   add_next_set_2( level-1, 0, old_base_sets, number_old_base);
	 else add_next_set_1( level-1, 0, old_base_sets, number_old_base);
       }
   }

 // then add the next set on level "level"

#ifdef STATS
  weight+=level;
#endif

  if (is_splitlevel)
   {number_sets++;
     for (whichset=next_to_add; whichset <number_all_subsets[level]; whichset++)
       {
	 buffer=all_subsets[level][whichset]; 
	 if (is_uc(old_base_sets,number_old_base,new_base_sets,&number_new_base_sets,buffer))
	   {
	     ucs[number_sets-1]=buffer; 
	     set_is_present[buffer]=1;
	     if (grpsize=minimal(level))
	       {output(grpsize); 
		 // when split, the smaller sets are output in part 0
		 if (number_sets<maxsets) 
		   {
		     if (level>minlevel)  add_next_set_2( level-1, 0, new_base_sets,number_new_base_sets); 
		     /* this must come first, because otherwise the automorphisms for this level are overwritten */
		     if (whichset <number_all_subsets[level]-1)
		       add_next_set_2( level, whichset+1, new_base_sets,number_new_base_sets);
		   }
	       }
	     set_is_present[buffer]=0;
	   }
       }
   } // end if no more splitting according to second split
  else // before splitlevel2
   {number_sets++;
     for (whichset=next_to_add; whichset <number_all_subsets[level]; whichset++)
       {
	 buffer=all_subsets[level][whichset]; 
	 if (is_uc(old_base_sets,number_old_base,new_base_sets,&number_new_base_sets,buffer))
	   {
	     ucs[number_sets-1]=buffer; 
	     set_is_present[buffer]=1;
	     if (grpsize=minimal(level))
	       {if (rest2==0)  { output(grpsize); }
		 // when split, the small sets are output in part 0
		 if (number_sets<maxsets) 
		   {
		     if (level>minlevel) add_next_set_1( level-1, 0, new_base_sets,number_new_base_sets); 
		     /* this must come first, because otherwise the automorphisms for this level are overwritten */
		     if (whichset <number_all_subsets[level]-1)
		       add_next_set_1( level, whichset+1, new_base_sets,number_new_base_sets);		 
		   }
	       }
	     set_is_present[buffer]=0;
	   }
       }
   } // end else -- restrictions to test
number_sets--;
#ifdef STATS
  weight-=level;
#endif


 if (next_to_add==0)  start_level[level]= -1;

 return;
}

void add_next_set( int level, int next_to_add, settype old_base_sets[], int number_old_base )
{
 settype new_base_sets[1<<(N)], buffer;
 int number_new_base_sets, whichset, is_splitlevel, grpsize;

 //write_ucs();

is_splitlevel=((number_sets - (level*3))>=splitlevel); // can increase by 4 in one step, so can be >

 if (mod && is_splitlevel) 
   {//splitcounter++; return;
       if (modcounter==mod) modcounter=1;
      else { modcounter++; return;} 
   }

  if (next_to_add==0) 
   { start_level[level]=number_sets;
     // first leave out level
     if (level>minlevel) 
       {
	 if (number_auts[level+1]==0) number_auts[level]=0;
	 else number_auts[level]= -1; // as a sign to look one level higher
	 if (is_splitlevel) 
	   { if (mod2==0) add_next_set_2( level-1, 0, old_base_sets, number_old_base);
	     else add_next_set_1( level-1, 0, old_base_sets, number_old_base);
	   }
	  else add_next_set( level-1, 0, old_base_sets, number_old_base);
       }
   }

 // then add the next set on level "level"

#ifdef STATS
  weight+=level;
#endif

  if (is_splitlevel)
   {number_sets++;
     for (whichset=next_to_add; whichset <number_all_subsets[level]; whichset++)
       {
	 buffer=all_subsets[level][whichset]; 
	 if (is_uc(old_base_sets,number_old_base,new_base_sets,&number_new_base_sets,buffer))
	   {
	     ucs[number_sets-1]=buffer; 
	     set_is_present[buffer]=1;
	     if (grpsize=minimal(level))
	       {if (rest2==0) output(grpsize); 
		 // when split, the smaller sets are output in part 0
		 if (number_sets<maxsets) 
		   {
		     if (level>minlevel)  
		       {  if (mod2==0)  add_next_set_2( level-1, 0, new_base_sets,number_new_base_sets); 
			 else add_next_set_1( level-1, 0, new_base_sets,number_new_base_sets); }
		     /* this must come first, because otherwise the automorphisms for this level are overwritten */
		     if (whichset <number_all_subsets[level]-1)
		       { if (mod2==0) add_next_set_2( level, whichset+1, new_base_sets,number_new_base_sets);
			 else add_next_set_1( level, whichset+1, new_base_sets,number_new_base_sets);
		       }
		   }
	       }
	     set_is_present[buffer]=0;
	   }
       }
   } // end if no more splitting according to first split
  else // before splitlevel
   {number_sets++;
     for (whichset=next_to_add; whichset <number_all_subsets[level]; whichset++)
       {
	 buffer=all_subsets[level][whichset]; 
	 if (is_uc(old_base_sets,number_old_base,new_base_sets,&number_new_base_sets,buffer))
	   {
	     ucs[number_sets-1]=buffer; 
	     set_is_present[buffer]=1;
	     if (grpsize=minimal(level))
	       {if ((rest==0) && (rest2==0)) output(grpsize); 
		 // when split, the small sets are output in part 0
		 if (number_sets<maxsets) 
		   {
		     if (level>minlevel) add_next_set( level-1, 0, new_base_sets,number_new_base_sets); 
		     /* this must come first, because otherwise the automorphisms for this level are overwritten */
		     if (whichset <number_all_subsets[level]-1)
		       add_next_set( level, whichset+1, new_base_sets,number_new_base_sets);		 
		   }
	       }
	     set_is_present[buffer]=0;
	   }
       }
   } // end else -- restrictions to test
number_sets--;
#ifdef STATS
  weight-=level;
#endif


 if (next_to_add==0)  start_level[level]= -1;

 return;
}

void start_generation()
{
settype base_sets[2];

  base_sets[0]=ucs[0]=(1U<<n)-1;
  set_is_present[base_sets[0]]=1;
  number_sets=1;
  start_level[n]=0;

#ifdef STATS
  weight=n;
#endif

  if ((rest==0) && (rest2==0)) output(nfak);

  if (n>minlevel) add_next_set(n-1,0,base_sets,1);

}


/*******************MAIN********************************/

int main(int argc,char *argv[])


{
  int i;

  if ((argc<2) || !isdigit(argv[1][0])) usage(argv[0]);
  n= atoi(argv[1]);

  if (n>N) { fprintf(stderr,"Number of elements may be at most %d -- exiting.\n",N);
                  exit(0); }

  if (n>7) { fprintf(stderr,"Implement other method for large group first, \n");
                  fprintf(stderr,"change datatype for labelled_structurecounter,\n");
		  fprintf(stderr,"and make sure that you have millions of years of CPU available :-) \n");
                  exit(0); }

  if (n<1) usage(argv[0]);

  for (i=2;i<argc;i++)
    {
      if (argv[i][0]=='m')
	{ if (argv[i][1]=='2')
	  {
	    if ((i+2>=argc) || !isdigit(argv[i+1][0]) || !isdigit(argv[i+2][0])) usage(argv[0]);
	    rest2=atoi(argv[++i]);
	    mod2=atoi(argv[++i]);
	    if ((mod2<1) || (rest2>=mod2)) usage(argv[0]);
	    if (n<=6) usage(argv[0]);
	    else { if (n==7) splitlevel2=43;
	      // the difference between splitlevel and splitlevel2 must be at least 4
	      else { fprintf(stderr,"First find good splitlevel2 for n>=8.\n"); exit(0); }
	    }
	    fprintf(stderr,"Splitlevel2=%d\n",splitlevel2);
	  }
	  else
	  { if ((i+2>=argc) || !isdigit(argv[i+1][0]) || !isdigit(argv[i+2][0])) usage(argv[0]);
	    rest=atoi(argv[++i]);
	    mod=atoi(argv[++i]);
	    if ((mod<1) || (rest>=mod)) usage(argv[0]);
	    if (n<=6) splitlevel=11; 
	    else { if (n==7) splitlevel=18;
	      else { fprintf(stderr,"First find good splitlevel for n>=8.\n"); exit(0); }
	    }
	    fprintf(stderr,"Splitlevel=%d\n",splitlevel);
	  }
	}
      else if (argv[i][0]=='s')
       {
	 if ((i+1>=argc) || !isdigit(argv[i+1][0])) usage(argv[0]);
	 maxsets=atoi(argv[++i]);
       }
      else if (argv[i][0]=='w') 
	{ writeout=1;
	  if (argv[i][1]=='b') writebinary=1;
	} 
      else if (argv[i][0]=='e')
       {
	 if ((i+1>=argc) || !isdigit(argv[i+1][0])) usage(argv[0]);
	 minlevel=atoi(argv[++i]);
	 if ((minlevel>n) || (minlevel<1)) usage(argv[0]);
       }
    }

  for (i=0;i<argc;i++) fprintf(stderr,"%s ",argv[i]); fprintf(stderr,"\n");

  if (mod2!=0 && mod==0) usage(argv[0]);

  modcounter= mod-rest;
  modcounter2= mod2-rest2;

  for (i=0;i<=n;i++) start_level[i] = -1;

  makepermutations(n);
  make_all_subsets();
  start_generation();

  /*for (i=0;i<number_permutations;i++)
    {
    fprintf(stderr,"permutation number %d -- Set \n",i);
    writeset(13);
    fprintf(stderr,"is mapped on\n");
    writeset(IMAGE(i,13));
    fprintf(stderr,"\n");
    }*/
  fprintf(stderr,"structurecounter=%ld\n",structurecounter);
  fprintf(stderr,"labelled structurecounter=%llu\n",labelled_structurecounter);

#ifdef TEST
  fprintf(stderr,"Of those being tested for uc were %ld union closed and %ld not.\n",not_uc, well_uc);
#endif

#ifdef STATS
  fprintf(stderr,"Number of sparse union closed sets (average size of set <= n/2): %ld\n",number_sparse);

  for (i=1;i<(1<<n); i++) 
    if  (structurecounter_numbersets[i]) fprintf(stderr,"With %d sets: %ld\n",i,structurecounter_numbersets[i]);

  for (i=1;i<(1<<n); i++) 
    if  (structurecounter_numbersets_sparse[i]) fprintf(stderr,"Sparse ucs with %d sets: %ld\n",i,structurecounter_numbersets_sparse[i]);



#endif 
  //    fprintf(stderr,"splitcounter %ld\n",splitcounter);
return(0);

}


