#include <math.h>
#include <stdio.h>
#include <time.h>

/**********************************************/
/**********************************************/
/* This program was written by Arjen Lenstra. */
/**********************************************/
/**********************************************/

/* there's not much explanation in this program, but what it does
   is more or less the following:	
   given some file containing relations (NEWRELATIONS) the program
   checks if it's the first run of the elimination process or not.
   If it is, then there's no file HOWFAR around, if it is not,
   the program will retrieve some info about the number of rows
   that have been processed already (in a previous run) and the
   number of solutions found from a file called HOWFAR and there
   will be a file ELIMINATED that contains those rows that
   have been processed (in diagonal form, densely and/or sparsely
   represented). The new relations will be processed (in blocks
   that fit in memory), using ELIMINATED if it exists, and the
   newly processed relations will be
   appended to ELIMINATED. HOWFAR will be updated for the next round
*/




#define	MAXNUMBERBYTES  10000
#define	BITS		32
#define	BITSM		31
#define	SHIFT		5

typedef long *row;
typedef row *mat;

static unsigned int mask [] =
	{		020000000000,	010000000000,
	004000000000,	002000000000,	001000000000,
	000400000000,	000200000000,	000100000000,
	000040000000,	000020000000,	000010000000,
	000004000000,	000002000000,	000001000000,
	000000400000,	000000200000,	000000100000,
	000000040000,	000000020000,	000000010000,
	000000004000,	000000002000,	000000001000,
	000000000400,	000000000200,	000000000100,
	000000000040,	000000000020,	000000000010,
	000000000004,	000000000002,	000000000001	};

long sizefacbase=0, actualnumberrows = 0, rowlength=0, maxnumberrows=0,
	totaldone=0, sols=0;
mat nextblock=NULL;
row victims=NULL, datarow=NULL, colikilledbyrow=NULL, maxindexnewdata=NULL;
short *sortiere;



static void
reverse(s)
        char            *s;
{
        int             c, i, j;

        for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
                c = *(s + i);
                *(s + i) = *(s + j);
                *(s + j) = c;
        }
}



static void
itoa(n, s, base)
        unsigned long   n;
        char            s[];
        int             base;
{
        unsigned long   i, digit, dig16;

        if (base > 16) {
                printf("\n itoa: maxbase = 16: exiting\n");
                exit(1);
        }
        i = 0;
        do {
                digit = n % base + '0';
                if (base == 16) {
                        dig16 = digit - '0';
                        switch (dig16) {
                        case 10:
                        case 11:
                        case 12:
                        case 13:
                        case 14:
                        case 15:
                                s[i++] = 'a' + dig16 - 10;
                                break;
                      default:
                                s[i++] = digit;
                                break;
                        }
                } else
                        s[i++] = digit;
        } while ((n /= base) > 0);
        s[i] = '\0';
        reverse(s);

}


char* get_name(s)
char *s;

{
   FILE *fp;
   static int first_one=1;
   static char *hilf;   /* static */
   static char *hilf2;  /* static */
   char *physical;


   physical= (char*) malloc(35 * sizeof(char));


   if (first_one)
       {
	first_one = 0;	/* SN: next time it's no longer the first time !! */
        hilf=(char*) malloc(19 * sizeof(char));
        hilf2=(char*) malloc(19 * sizeof(char));

        if ((fp=fopen("/tmp/FAKT", "w"))==NULL)
           { strcpy(hilf,"./");
             strcpy(hilf2,".\\");
             itoa(getpid(),physical,10);
             strcat(hilf,physical);
             strcat(hilf2,physical);

           }

         else
	     { fclose(fp); unlink("/tmp/FAKT");
               strcpy(hilf,"/tmp/");
               strcpy(hilf2,"\\tmp\\");

               itoa(getpid(),physical,10);
               strcat( hilf,physical);
               strcat(hilf2,physical);
	
             }


       }


#ifndef __EMX__

if (!strcmp(s,"FAKM"))
    {  strcpy(physical,hilf);
       strcat(physical,"FAKM");
    }
if (!strcmp(s,"ALLS"))
    {  strcpy(physical,hilf);
       strcat(physical,"ALLS");
    }
if (!strcmp(s,"NEWR"))
   { strcpy(physical,hilf);
     strcat(physical,"NEWR");
   }
if (!strcmp(s,"ELIM"))
   { strcpy(physical,hilf);
     strcat(physical,"ELIM");
   }

if (!strcmp(s,"SOLU"))
    { strcpy(physical,hilf);
     strcat(physical,"SOLU");
    }
if (!strcmp(s,"sort"))
   { strcpy(physical,hilf);
    strcat(physical,"sort");
   }
if (!strcmp(s,"REL"))
   { strcpy(physical,hilf);
    strcat(physical,"REL");
   }

if (!strcmp(s,"HOWF"))
  { strcpy(physical,hilf);
    strcat(physical,"HOWF");
  }

if (!strcmp(s,"PART"))
  { strcpy(physical,hilf);
    strcat(physical,"PART");
  }

if (!strcmp(s,"UMSP"))
  { strcpy(physical,hilf);
    strcat(physical,"UMSP");
  }

if (!strcmp(s,"NEUE"))
  { strcpy(physical,hilf);
    strcat(physical,"NEUE");
  }
if (!strcmp(s,"ZUSA"))
  { strcpy(physical,hilf);
    strcat(physical,"ZUSA");
  }

#else

if (!strcmp(s,"FAKM"))
  {  strcpy(physical,hilf2);
       strcat(physical,"FAKM");
  }
if (!strcmp(s,"ALLS"))
  {  strcpy(physical,hilf2);
       strcat(physical,"ALLS");
  }

if (!strcmp(s,"NEWR"))
  { strcpy(physical,hilf2);
    strcat(physical,"NEWR");
  }

if (!strcmp(s,"REL"))
  { strcpy(physical,hilf2);
    strcat(physical,"REL");
  }

if (!strcmp(s,"SOLU"))
  { strcpy(physical,hilf2);
    strcat(physical,"SOLU");
  }


if (!strcmp(s,"ELIM"))
  { strcpy(physical,hilf2);
     strcat(physical,"ELIM");
  }

if (!strcmp(s,"sort"))
  { strcpy(physical,hilf2);
    strcat(physical,"sort");
  }

if (!strcmp(s,"HOWF"))
  { strcpy(physical,hilf2);
    strcat(physical,"HOWF");
  }

if (!strcmp(s,"PART"))
  { strcpy(physical,hilf2);
    strcat(physical,"PART");
  }

if (!strcmp(s,"UMSP"))
  { strcpy(physical,hilf2);
    strcat(physical,"UMSP");
  }

if (!strcmp(s,"NEUE"))
  { strcpy(physical,hilf2);
    strcat(physical,"NEUE");
  }
if (!strcmp(s,"ZUSA"))
  { strcpy(physical,hilf2);
    strcat(physical,"ZUSA");
  }

#endif
return(physical);

}




int processblock ()
{

 FILE *olddata;
 FILE *solution;
 char command1[50], command2[50], command3[50];

   long i, j, maxand, maxshift;
    mat locblock, locblockj;
   long k, length, maxini, newsparses = 0, oldsparses = 0,
       olddenses = 0, selims = 0, delims = 0, vicn, often = 500;
   double avsel, avdel, avweight = 0.0;



  if ((olddata = fopen(get_name("ELIM"),"r")) != NULL)
  {

                  /* process the old data first */
    for (k=1; k<=totaldone; k++)
      {
      locblock = nextblock;
      fread(&length,sizeof(long),1,olddata);
      if (length < 0) continue;
      fread(&maxini,sizeof(long),1,olddata);
      colikilledbyrow[maxini]= k;

      maxshift = maxini >> SHIFT;
      maxand = mask[maxini & BITSM];
      if (length <= rowlength)
      { /*SPARSE */
	oldsparses ++; vicn = 0; avweight += (double)length;
	for (i=0; i<actualnumberrows; i++)
	  if ((*locblock++)[maxshift]&maxand) 	    victims[vicn++] = i;
         	fread(datarow,sizeof(row),length,olddata);
	if (vicn > 0)
	{
	  selims += vicn;
	  for (j=0; j<length; j++)
	  {
	    maxshift = datarow[j] >> SHIFT;
	    maxand = mask[datarow[j] & BITSM];
	    for (i=0; i<vicn; i++)
	      nextblock[victims[i]][maxshift] ^= maxand;
	  }
	}
      }
      else
      { /*DENSE*/
	olddenses++;
		fread(datarow,sizeof(long),rowlength,olddata);
	locblock = nextblock;
	for (i=0; i<actualnumberrows; i++)
	{
	  if ((maxindexnewdata[i] >= maxini) && (((*locblock)[maxshift]&maxand)!=0))
	  {
	    register long *l = *locblock,
	    *d = datarow;
	    j = rowlength >> 3;
	                       /* now be careful, this is not */
	                       /* supposed to be clear, only fast */
	                       /* and may be dirty. have fun */
	
	    switch (rowlength & 7)
	    {
	      do {
	      case 0: j--; *l++ ^= *d++;
	      case 7: *l++ ^= *d++;
	      case 6: *l++ ^= *d++;
	      case 5: *l++ ^= *d++;
	      case 4: *l++ ^= *d++;
	      case 3: *l++ ^= *d++;
	      case 2: *l++ ^= *d++;
	      case 1: *l++ ^= *d++;
	      } while (j);
	    }
	    delims ++;
	  }
	  locblock ++;
	}
      }
      if ((k%often)==0)
      {
	avsel = 0; avdel = 0;
	if (oldsparses !=0)
	  avsel = (double)selims/(actualnumberrows*oldsparses);
	if (olddenses !=0)
	{
	  avdel = (double)delims/(actualnumberrows*olddenses);
	  if (avdel >0.1)
	    often = 100;
	}
      }
    }
    fclose(olddata);
    avsel = 0; avdel = 0;
    if (oldsparses !=0)
    {
      avsel = (double)selims/(actualnumberrows*oldsparses);
      avweight /= (double)oldsparses;
    }
    if (olddenses !=0)
      avdel = (double)delims/(actualnumberrows*olddenses);

    avsel = (double)(selims+delims)/(actualnumberrows*totaldone);
    avweight *= (double)oldsparses;
  }

  olddata = fopen(get_name("ELIM"),"a+");
  locblock = nextblock;
  solution = fopen(get_name("ALLS"),"a");  /* SN: Open ALLS just once!! */
                                           /*     (i.e. before loop)    */
  for (k=0; k<actualnumberrows; k++)
  {
    totaldone ++;
    maxini = maxindexnewdata[k]+1;
    while ((--maxini) >= 0)
      if ((((*locblock)[maxini>>SHIFT]&mask[maxini&BITSM]) !=0) &&
	  (colikilledbyrow[maxini] == 0))
	break;


    if (maxini >= 0)
    {
      maxshift = maxini>>SHIFT;
      maxand = mask[maxini&BITSM];
      (*locblock)[maxshift] ^= maxand; /*kill the maximum*/
      colikilledbyrow[maxini] = totaldone;

      length =0;
      for (j=0;j<rowlength;j++)
      {
	if ((*locblock)[j]!=0)
	  for (i=BITSM;i>=0;i--)
	    if (((*locblock)[j]&mask[i]) != 0)
	    {
	      datarow[length++] = (j << SHIFT) + i;
	      /*notice that here we need datarow[rowlength], may be */
	      if (length > rowlength)
	      {
	       	fwrite(&length,sizeof(long),1,olddata);
		fwrite(&maxini,sizeof(long),1,olddata);
		fwrite((*locblock),sizeof(long),rowlength,olddata);

		goto quitloop;
	      }
	    }
      }
      fwrite(&length,sizeof(long),1,olddata);
      fwrite(&maxini,sizeof(long),1,olddata);
      newsparses ++; avweight += (double)length;
      fwrite(datarow,sizeof(long),length,olddata);

    quitloop:
      locblockj = locblock+1;
      for (j=k+1; j<actualnumberrows; j++)
      {
	if (((*locblockj)[maxshift]&maxand) !=0 )
	{
	  register long *lj = *locblockj, *l = *locblock;
	
	  i = rowlength >> 3;
	  switch (rowlength & 7)
	  {
	    do {
	    case 0: i--; *lj++ ^= *l++;
	    case 7: *lj++ ^= *l++;
	    case 6: *lj++ ^= *l++;
	    case 5: *lj++ ^= *l++;
	    case 4: *lj++ ^= *l++;
	    case 3: *lj++ ^= *l++;
	    case 2: *lj++ ^= *l++;
	    case 1: *lj++ ^= *l++;
	    } while (i);
	  }
	}
	locblockj ++;
      }
    }
    else
    { /* solution found */

      sols ++;
      for (j=0;j<=sizefacbase;j++)
	if (((*locblock)[j>>SHIFT]&mask[j&BITSM]) !=0 )
           sortiere[colikilledbyrow[j]]=1;





      for (j=0; j<=sizefacbase;j++)
         { if (sortiere[j]==1)
             fprintf(solution,"%ld\n",j);
           sortiere[j]=0;
         }


	  /*	     fprintf(solution,"%ld\n",colikilledbyrow[j]);   */

        fprintf(solution,"%ld\n",totaldone);
        fprintf(solution,"7654321\n");

	/*      sprintf(command1,"sort -n %s >%s",get_name("SOLU"),get_name("sort"));
      sprintf(command2,"mv %s %s",get_name("sort"), get_name("SOLU"));
      */

/* SN: Append to ALLS imidiately, instead of writing SOLU and 
       then appending to ALLS by using cat !!!   		*/
/* #ifdef __EMX__ */
/*       sprintf(command3,"type %s >>%s",get_name("SOLU"), get_name("ALLS")); */
/* #else */
/*       sprintf(command3,"cat %s >>%s",get_name("SOLU"), get_name("ALLS")); */
/* #endif */

/*               system(command1); */
/*               system(command2); */

/*       system(command3); */

/*       unlink(get_name("SOLU")); */

/*      system("sort -n SOLUTION >sortsol.dd");
      system("mv sortsol.dd SOLUTION");
      system("cat SOLUTION >>ALLSOLUTIONS");
      unlink("SOLUTION");*/


      /* dump this row to get counter on olddata correct*/      fwrite(&maxini,sizeof(long),1,olddata);  /* maxini < 0 */
    }
    locblock ++;
  }
  fclose(olddata);
  fclose(solution);
  oldsparses += newsparses;
  if (oldsparses!=0)      avweight /= (double)oldsparses;
}


void qs_gauss(size_FB)
long size_FB;

{
  FILE *howfar;
  FILE *newdata;




  register long i, j;
  long dummy, pairs, where, maxindex;
  mat locblock;
  char command[50];


    sizefacbase=0; actualnumberrows = 0; rowlength=0; maxnumberrows=0; totaldone=0; sols=0;



  /*  if (first) {sizefacbase=0;actualnumberrows = 0; rowlength=0; maxnumberrows=0; totaldone=0; sols=0; first--;}
   */

  /* sizefacbase=0;actualnumberrows = 0; rowlength=0; maxnumberrows=0; totaldone=0; sols=0; first--;
   */

  /*  unlink(get_name("ELIM")); */ /* NEU */
  /*  unlink(get_name("HOWF")); */

  if ((newdata = fopen(get_name("NEWR"),"r")) == NULL ) {
    printf("no new data to be processed, exit\n");
    exit(0);
  }


  if ((howfar = fopen(get_name("HOWF"),"r")) == NULL ) {


    unlink(get_name("ELIM"));
    unlink(get_name("HOWF"));
    totaldone = 0; sols = 0;
   }
  else
  {
    fscanf(howfar,"%d %d", &totaldone, &sols);
    fclose(howfar);
  }

  sizefacbase = size_FB;

  /* should be able to store sizefacbase+1 bits per row */
  if ((colikilledbyrow = (long*) calloc(sizefacbase+1,sizeof(long)))==NULL)
    {printf("couldn't allocate 1, exit\n"); exit(0); }

  /* We make use of the fact that colikilledbyrow contains zero's now */

  rowlength = sizefacbase/BITS +1;

  maxnumberrows = MAXNUMBERBYTES / (sizeof(long)*rowlength);

  if (maxnumberrows == 0) {
    printf("not enough memory, increase MAXNUMBERBYTES, exit\n");
    exit(0);
  }

  if ((datarow = (long*) calloc(rowlength+1,sizeof(long))) == NULL)
    {printf("couldn't allocate 2, exit\n"); exit(0); }

  if ((victims = (long*) calloc(maxnumberrows,sizeof(long))) == NULL)
    {printf("couldn't allocate 3, exit\n"); exit(0); }

  if ((maxindexnewdata = (long*) calloc(maxnumberrows,sizeof(long))) == NULL)
    {printf("couldn't allocate 4, exit\n"); exit(0); }


  if ((nextblock = (row*) calloc(maxnumberrows,sizeof(row))) == NULL)
    {printf("couldn't allocate 5, exit\n"); exit(0); }

  sortiere= (short*) calloc((sizefacbase+1),sizeof(short));
  memset(sortiere, 0, (sizefacbase+1)*sizeof(short));

  locblock= nextblock;
  for (i=0; i<maxnumberrows; i++)
    { if ((*locblock++ = (long*) calloc(rowlength,sizeof(long))) == NULL)
      {printf("couldn't allocate %ld, exit\n",6+i); exit(0); }

    }


#ifdef __EMX__
  sprintf(command,"type %s >>%s",get_name("NEWR"), get_name("REL"));
#else
   sprintf(command,"cat %s >>%s",get_name("NEWR"), get_name("REL"));
#endif
  system(command);

/*  system("cat NEWRELATIONS >>RELATIONS"); */

  locblock = nextblock;
  while (fscanf(newdata,"%ld",&dummy) != EOF) {
    /* get next line from newdata */
    maxindex = -1;
    for (j=0; j<rowlength; j++) (*locblock)[j] = 00000000;
    fscanf(newdata,"%ld %ld",&pairs,&dummy);
    if (dummy < 0) {
      maxindex = 0;
      (*locblock)[0] = mask[0];
    }
    for (j=pairs/2; j>0; j--) {
      fscanf(newdata,"%ld %ld",&dummy,&where);
      if ((dummy&1) != 0) {
	maxindex = where;
	(*locblock)[where>>SHIFT] |= mask[where&BITSM];
      }
    }
    maxindexnewdata[actualnumberrows] = maxindex;
    if (maxindex < 0)
      printf("----> line %ld of NEWRELATIONS has only even exponents\n",
	     actualnumberrows+1);
    fscanf(newdata,"%ld",&dummy);
    for (j=dummy; j>0; j--) fscanf(newdata,"%ld",&where);

    /* got line, if enough process them */
    if ((++actualnumberrows) == maxnumberrows) {
      /* process them from 0 to actualnumberrows-1*/
      processblock();

      actualnumberrows = 0;
      locblock = nextblock;
    }
    else locblock ++;
  }
  if (actualnumberrows > 0 ) processblock();

  fclose(newdata);

  howfar = fopen(get_name("HOWF"),"w");
  fprintf(howfar,"%d %d\n",totaldone,sols);
  fclose(howfar);

  locblock = nextblock;
  for (i=0; i<maxnumberrows; i++)
    free(*locblock++);

  free(nextblock);
  locblock = NULL;
  free(victims);
  free(datarow);
  free(colikilledbyrow);
  free(maxindexnewdata);
}
