
#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:gf2n.h>
#else
#include <LiDIA/gf2n.h>
#endif

unsigned int gf2n::exp1=0, gf2n::exp2=0, gf2n::exp3=0;   /* set in gf2n_init */

/* ============================================================== */
/* Function:  partial_reduce1                                     */
/* ============================================================== */
/*                                                                */
/*  reduce all array elements with indices >= gf2n::anzBI to zero */
/*  Note: for complete reduction, the coefficients for x^k with   */
/*  k > extension degree have to be reduced, too.                 */
/*  --> partial_reduce2                                           */
/*                                                                */
/*  special versions for trinomials, pentinomials                 */
/* ============================================================== */


void tri_partial_reduce1(udigit *f)
{
  register unsigned int i, k, l, w;
  register unsigned int anzBI = gf2n::anzBI, degree = gf2n::degree;
  register udigit h;

  i = 2*anzBI - 1;

  while(i >= anzBI)
    {
      h = f[i];
      if (!h)
	{
	  i--;
	  continue;
	}

      k = 8*sizeof(udigit)*i - degree;  
      f[i--] = (udigit) 0;              /* x^degree  */

      w = k / (8*sizeof(udigit));       /*  x^0       */
      l = k % (8*sizeof(udigit));

      if (l != 0)
	{
	  f[w] ^= (h << l);
	  f[w+1] ^= (h >> (8*sizeof(udigit)-l));
	}
      else
	f[w] ^= h;
      
      w = (k + gf2n::exp1) / (8*sizeof(udigit));
      l = (k + gf2n::exp1) % (8*sizeof(udigit));

      if (l != 0)
	{
	  f[w] ^= (h << l);
	  f[w+1] ^= (h >> (8*sizeof(udigit)-l));
	}
      else
	f[w] ^= h;
    }
}


void pent_partial_reduce1(udigit *f)
{
  register unsigned int i, k, l, w;
  register unsigned int anzBI = gf2n::anzBI, degree = gf2n::degree;
  register udigit h;

  i = 2*anzBI - 1;

  while(i >= anzBI)
    {
      h = f[i];
      if (!h)
	{
	  i--;
	  continue;
	}

      k = 8*sizeof(udigit)*i - degree;  
      f[i--] = (udigit) 0;              /* x^degree  */

      w = k / (8*sizeof(udigit));       /*  x^0      */
      l = k % (8*sizeof(udigit));

      if (l != 0)
	{
	  f[w] ^= (h << l);
	  f[w+1] ^= (h >> (8*sizeof(udigit)-l));
	}
      else
	f[w] ^= h;
      
      w = (k + gf2n::exp1) / (8*sizeof(udigit));
      l = (k + gf2n::exp1) % (8*sizeof(udigit));

      if (l != 0)
	{
	  f[w] ^= (h << l);
	  f[w+1] ^= (h >> (8*sizeof(udigit)-l));
	}
      else
	f[w] ^= h;
      
      w = (k + gf2n::exp2) / (8*sizeof(udigit));
      l = (k + gf2n::exp2) % (8*sizeof(udigit));

      if (l != 0)
	{
	  f[w] ^= (h << l);
	  f[w+1] ^= (h >> (8*sizeof(udigit)-l));
	}
      else
	f[w] ^= h;

      w = (k + gf2n::exp3) / (8*sizeof(udigit));
      l = (k + gf2n::exp3) % (8*sizeof(udigit));

      if (l != 0)
	{
	  f[w] ^= (h << l);
	  f[w+1] ^= (h >> (8*sizeof(udigit)-l));
	}
      else
	f[w] ^= h;
    }
}


void general_partial_reduce1(udigit *f)
{
  register unsigned int i, j, k, l, w, s;
  register unsigned int anzBI = gf2n::anzBI, degree = gf2n::degree;
  register unsigned int anz_expo = gf2n::anz_exponents;
  register udigit h;

  i = 2*anzBI - 1;

  while(i >= anzBI)
    {
      h = f[i];
      if (!h)
        {
          i--;
          continue;
        }

      k = 8*sizeof(udigit)*i - degree;
      f[i] = (udigit) 0;

      w = k / (8*sizeof(udigit));
      l = k % (8*sizeof(udigit));

      if (l != 0)
        {
          f[w] ^= (h << l);
         f[w+1] ^= (h >> (8*sizeof(udigit)-l));
        }
      else
        f[w] ^= h;
      
      for (j=0; j<anz_expo; j++) 
         {
           s = k + gf2n::exponents[j];
           w = s / (8*sizeof(udigit));
           l = s % (8*sizeof(udigit));

           if (l != 0)
             {
               f[w] ^= (h << l);
               f[w+1] ^= (h >> (8*sizeof(udigit)-l));
             }
           else
             f[w] ^= h;
         }
    }
}


/******* partial_reduce1 - selector *********************************/

void ( *partial_reduce1[] ) (udigit*) =
{
tri_partial_reduce1,  pent_partial_reduce1, general_partial_reduce1
};


/* ============================================================== */
/* Function:  partial_reduce2                                     */
/* ============================================================== */
/* we assume that partial_reduce1 has been used  and only the     */
/* word with index (gf2n::anzBI-1) may be unreduced               */
/*                                                                */
/*  special versions for trinomials, pentinomials                 */
/* ============================================================== */


void tri_partial_reduce2(udigit *f)
{
  register unsigned int l, w, deg, anz;
  register udigit h;

  anz = gf2n::anzBI-1;
  deg = gf2n::degree % (8*sizeof(udigit));
  h = f[anz] >> deg;

  f[0] ^= h;
  f[anz] ^= (h << deg);
  
  w = gf2n::exp1 / (8*sizeof(udigit));
  l = gf2n::exp1 % (8*sizeof(udigit));
  
  if (l != 0)
    {
      f[w] ^= (h << l);
      f[w+1] ^= (h >> (8*sizeof(udigit)-l));
    }
  else
    f[w] ^= h;
}


void pent_partial_reduce2(udigit *f)
{
  register unsigned int l, w, anz, deg;
  register udigit h;

  anz = gf2n::anzBI-1;
  deg = gf2n::degree % (8*sizeof(udigit));
  h = f[anz] >> deg;

  f[0] ^= h;
  f[anz] ^= (h << deg);
  
  w = gf2n::exp1 / (8*sizeof(udigit));
  l = gf2n::exp1 % (8*sizeof(udigit));
  
  if (l != 0)
    {
      f[w] ^= (h << l);
      f[w+1] ^= (h >> (8*sizeof(udigit)-l));
    }
  else
    f[w] ^= h;

  w = gf2n::exp2 / (8*sizeof(udigit));
  l = gf2n::exp2 % (8*sizeof(udigit));
  
  if (l != 0)
    {
      f[w] ^= (h << l);
      f[w+1] ^= (h >> (8*sizeof(udigit)-l));
    }
  else
    f[w] ^= h;

  w = gf2n::exp3 / (8*sizeof(udigit));
  l = gf2n::exp3 % (8*sizeof(udigit));
  
  if (l != 0)
    {
      f[w] ^= (h << l);
      f[w+1] ^= (h >> (8*sizeof(udigit)-l));
    }
  else
    f[w] ^= h;
}



void general_partial_reduce2(udigit *f)
{
  register unsigned int j, s, l, w, anz, deg;
  register udigit h;

  anz = gf2n::anzBI-1;
  deg = gf2n::degree % (8*sizeof(udigit));
  h = f[anz] >> deg;

  while (h != 0)
    { 
      f[0] ^= h;
      f[anz] ^= (h << deg);

      for (j=0; j < gf2n::anz_exponents; j++) 
	{
	  s = gf2n::exponents[j];
	  w = s / (8*sizeof(udigit));
	  l = s % (8*sizeof(udigit));
	  
	  if (l != 0)
	    {
	      f[w] ^= (h << l);
	      f[w+1] ^= (h >> (8*sizeof(udigit)-l));
	    }
	  else
	    f[w] ^= h;
	}
      h = f[anz] >> deg;
    }
}


/******* partial_reduce2 - selector *********************************/

void ( *partial_reduce2[] ) (udigit*) = 
{
tri_partial_reduce2,  pent_partial_reduce2, general_partial_reduce2
};








/* ====================================================================== */
/*                   FAST INVERSION, BINARY GCD ALGORITHM                 */
/* ====================================================================== */

inline udigit max(udigit a, udigit b)
{
  if (a >= b)
    return a;
  else return b;
}

/************************************************************************
  shift F k bits to the left, adjust Flast 
  **********************************************************************/

inline void shift_left(udigit *F, unsigned int k, unsigned int & Flast)
{
  register int i, j, s;
  register udigit *fp1, *fp2; 
 
  s = k % (8*sizeof(udigit));
  j = k / (8*sizeof(udigit));

  if (s == 0)
    {
      for (i=Flast, fp1=F+Flast+j, fp2=F+Flast; i >= 0; i--, fp1--, fp2--) 
	*fp1 = *fp2;
      Flast += j;
    }
  else
    {
      for (i=Flast, fp1=F+Flast+j+1, fp2=F+Flast; i >= 0; i--, fp2--)
	{
	  (*fp1) |= ((*fp2) >> (8*sizeof(udigit)-s));
	  fp1--;
	  (*fp1)  = ((*fp2) << s);
	}
      Flast += j+1;
    } 

  i = 0; fp1 = F;
  while(i < j)
    {
      *fp1 = (udigit) 0;
      i++; fp1++;
    }
  
  fp1 = F + Flast;
  while ((*fp1) == (udigit) 0 && Flast > 0)
    {
      Flast --;
      fp1--;
    }
}


/************************************************************************
  shift F k bits to the right, adjust Flast 
  **********************************************************************/

inline void shift_right(udigit *F, unsigned int k, unsigned int & Flast)
{
  register unsigned int j, i, s;
  register udigit *fp1, *fp2;

  s = k % (8*sizeof(udigit));
  j = k / (8*sizeof(udigit));

  if (s == 0)
    {
      for (i=j, fp1=F, fp2=F+j; i <= Flast; i++, fp1++, fp2++) 
	*fp1 = *fp2;
    }
  else
    {
      for (i=j, fp1=F, fp2=F+j; i <= Flast; i++, fp1++, fp2++)
	*fp1 = ((*fp2) >> s) | ((*(fp2+1)) << (8*sizeof(udigit)-s));
    } 
  i = Flast-j+1; 
  fp1 = F+i;
  while(i <= Flast)
    {
      *fp1 = (udigit) 0;
      fp1++;
      i++;
    }

  Flast -= j; 
  fp1 = F+Flast;
  while (*fp1 == (udigit) 0 && Flast > 0)
    {
      Flast --; fp1--;
    }
}


/************************************************************************
  compute F = (F + G) / x^k, adjust Flast 
  **********************************************************************/

inline void add_shift_right(udigit *F, udigit*G, unsigned int k,
			    unsigned int & Flast, unsigned int Glast) 
  
{ 
  register unsigned int j, i, s, l;
  register udigit h;
  register udigit *fp1, *fp2, *gp;

  s = k % (8*sizeof(udigit));
  j = k / (8*sizeof(udigit));

  l = (unsigned int) max(Flast, Glast);

  if (s == 0)
    {
      for (i=j, fp1=F, fp2=F+j, gp=G+j; i <= l; 
	   i++, fp1++, fp2++, gp++) 
	*fp1 = (*fp2) ^ (*gp);
    }
  else
    {
      fp2 = F+j; gp=G+j;
      h = (*fp2) ^ (*gp);
      for (i=j, fp1=F; i <= l; i++, fp1++)
	{
	  *fp1 = (h >> s);
	  fp2++; gp++;
	  h = (*fp2) ^ (*gp);
	  *fp1 |= (h << (8*sizeof(udigit)-s));
	}
    } 
  i = l-j+1; 
  fp1 = F+i;
  while(i <= Flast)
    {
      *fp1 = (udigit) 0; 
      i++; fp1++;
    }


  Flast = l - j;
  fp1 = F+Flast;
  while (*fp1 == (udigit) 0 && Flast > 0)
    {
      Flast --;
      fp1--;
    }
}




/*********************************************************************
  invert function, res and a are assumed to have length gf2n::anzBI
  *******************************************************************/

void tri_invert(udigit *res, udigit *a)
{
  register udigit h;
  register udigit *bp, *cp, *fp, *gp, *ap;
  unsigned int i, j, w, l;
  unsigned int xdegree=0;
  udigit *B, *C, *F, *G;
  unsigned int anzBI = gf2n::anzBI;
  unsigned int Clast, Blast, Flast, Glast;

  tri_partial_reduce2(a);        /* make sure that a is totally reduced  */

  B = bp = new udigit[2*anzBI];  /* allocate memory for all array variables */
  C = cp = new udigit[2*anzBI];
  F = fp = new udigit[2*anzBI];
  G = gp = new udigit[2*anzBI];

  if (( B == NULL) || (C == NULL) || (F==NULL) || (G==NULL))
    lidia_error_handler("gf2n","invert()::can't allocate memory");
  
  for (i=0, ap=a; i < anzBI; i++, fp++, gp++, cp++, bp++, ap++)  
                /* initialize B=1, C = 0, F = a, G = modulus, res = 0 */
    {
      *fp = *ap;
      *gp = *bp = *cp = (udigit) 0;
    }

  B[0] = (udigit) 1;

  for(i=anzBI, bp=B+anzBI, cp=C+anzBI, fp=F+anzBI, gp=G+anzBI; 
      i<2*anzBI; i++, bp++, cp++, fp++, gp++)
    {
      *fp = *gp = *cp = *bp = (udigit) 0;
    }

  G[0] = (udigit) 1;  
  i = gf2n::degree;
  G[i/(8*sizeof(udigit))] ^= udigit (1 << (i % (8*sizeof(udigit))));
  G[gf2n::exp1/(8*sizeof(udigit))] ^= udigit (1 << (gf2n::exp1 % (8*sizeof(udigit))));

  Clast = Blast  = 0;        /* initialize last non zero indices */
  Flast = Glast = anzBI-1;

  fp = F+Flast;
  while(*fp == (udigit) 0 && Flast > 0)
    {
      Flast --; fp--;
    }
  
  /*************************************************************
    start with gcd-computation, use the invariant:

                 B * F + ?? * m = F 
                 C * F + ?? * m = G

   ****************************************************************/


                         /* make F odd, if necessary */
  h = F[0];
  fp = F;

  if (!(h & 1))
    {
      while(h == (udigit) 0)
	{
	  fp++;
	  xdegree  += 8*sizeof(udigit);
	  h = *fp;
	}
      
      while (!(h & 1))
	{
	  xdegree++;
	  h >>= 1;
	}
      shift_right(F, xdegree, Flast);

      if (Flast == 0 && F[0] == 1)
	goto final_step;
    }

  

  while (1) 
    {                               /* degree(G) > degree(F)*/
      do
	{ 
	  j = (unsigned int) max(Clast, Blast);
          for (i=0, cp = C, bp = B; i<=j; i++, cp++, bp++)   /* C = C+B */
            (*cp) ^= (*bp);
	  
          Clast = j; 
	  cp--;
          while(*cp == (udigit) 0 && Clast > 0)
	    {
	      Clast -- ; 
	      cp--;
	    }
	  
	  h = G[0] ^ F[0];
	  i = 0; 
	  
	  while(h == (udigit) 0)
	    {
	      xdegree += 8*sizeof(udigit);
	      i += 8*sizeof(udigit);
	      h = G[i/(8*sizeof(udigit))] ^ F[i/(8*sizeof(udigit))];
	    }
	  
	  while (!(h & 1))
	    {
	      xdegree++;
	      i++;
	      h >>= 1;
	    }
	  add_shift_right(G, F, i, Glast, Flast);
	  shift_left(B, i, Blast);
	  
	  if (Glast == 0 && G[0] == (udigit) 1)          /* G == 1  ?? */
	    goto final_step;
	}
      while (Glast > Flast || ((G[Glast] >= F[Flast]) && (Glast == Flast)));

      do                                     /* deg F > deg G  */
	{
          j = (unsigned int) max(Clast, Blast);
          for (i=0, bp = B, cp = C; i<=j; i++, bp++, cp++)   /* B = B + C */
            (*bp) ^= (*cp);

          Blast = j; 
	  bp--;
          while (*bp == 0 && Blast > 0)
	    {
	      Blast --;
	      bp--;
	    }
                                      
          i = 0;                       /* make F odd */
          h = F[0] ^ G[0];
	  
	  while(h == (udigit) 0)
	    {
	      xdegree += 8*sizeof(udigit);
	      i += 8*sizeof(udigit);
	      h = F[i/(8*sizeof(udigit))] ^ G[i/(8*sizeof(udigit))];
	    }
	  
	  while (!(h & 1))
	    {
	      xdegree++;
	      i ++;
	      h >>= 1;
	    }
	  add_shift_right(F, G, i, Flast, Glast);
          shift_left(C, i, Clast);
	  
	  if (Flast == 0 && F[0] == (udigit) 1)   /* F == 1 */
	    goto final_step;
	} 
      while(Flast > Glast || ((F[Flast] >= G[Glast]) && (Flast == Glast)));
    }
 

final_step:       /* check whether F == 1 mod modulus or G == 1 mod modulus */

  if (G[0] == (udigit) 1 && Glast == 0)
    { 
      delete[] B;
      B = C;
      Blast = Clast;
    }

  /* now B == x^(xdegree) mod modulus and B has last index Blast */

  while (xdegree >= 8*sizeof(udigit))
    {
      xdegree -= 8*sizeof(udigit);
      h = B[0];
      
      B[0] = 0;
      w = gf2n::degree / (8*sizeof(udigit));
      l = gf2n::degree % (8*sizeof(udigit));
      Blast = w;
      
      if (l != 0)
	{
	  B[w] ^= (h << l);
	  B[w+1] ^= (h >> (8*sizeof(udigit)-l));
	  Blast++;
	}
      else 
	B[w] ^= h;
      
      w = gf2n::exp1 / (8*sizeof(udigit));
      l = gf2n::exp1 % (8*sizeof(udigit));
      
      if (l != 0)
	{
	  B[w] ^= (h << l);
	  B[w+1] ^= (h >> (8*sizeof(udigit)-l));
	}
      else
	B[w] ^= h;
      
      shift_right(B, 8*sizeof(udigit), Blast); 
    }
  
  h = B[0] & ((1<<xdegree)-1);     
  B[0] ^= h;
  w = gf2n::degree / (8*sizeof(udigit));
  l = gf2n::degree % (8*sizeof(udigit));
  Blast = w;
  if (l != 0)
    {
      B[w] ^= (h << l);
      B[w+1] ^= (h >> (8*sizeof(udigit)-l));
      Blast++;
    }
  else B[w] ^= h;
  
  w = gf2n::exp1 / (8*sizeof(udigit));
  l = gf2n::exp1 % (8*sizeof(udigit));
	      
  if (l != 0)
    {
      B[w] ^= (h << l);
      B[w+1] ^= (h >> (8*sizeof(udigit)-l));
    }
  else B[w] ^= h;
  
  shift_right(B, xdegree, Blast);

  for (i=0, bp=B, cp=res; i<=Blast; i++, bp++, cp++)  /* copy B into res */
    *cp= *bp;
     
  if (Blast < anzBI-1)
    for(i=Blast+1; i<anzBI; i++, cp++)
      *cp = (udigit) 0;
  
  delete[] B;
  delete[] F;
  delete[] G;
}

/******************************************************************/


void pent_invert(udigit *res, udigit *a)
{
  register udigit h;
  register udigit *bp, *cp, *fp, *gp, *ap;
  unsigned int i, j, w, l;
  unsigned int xdegree=0;
  udigit *B, *C, *F, *G;
  unsigned int anzBI = gf2n::anzBI;
  unsigned int Clast, Blast, Flast, Glast;

  pent_partial_reduce2(a);        /* make sure that a is totally reduced  */

  B = bp = new udigit[2*anzBI];  /* allocate memory for all array variables */
  C = cp = new udigit[2*anzBI];
  F = fp = new udigit[2*anzBI];
  G = gp = new udigit[2*anzBI];

  if (( B == NULL) || (C == NULL) || (F==NULL) || (G==NULL))
    lidia_error_handler("gf2n","invert()::can't allocate memory");
  
  for (i=0, ap=a; i < anzBI; i++, fp++, gp++, cp++, bp++, ap++)  
                /* initialize B=1, C = 0, F = a, G = modulus, res = 0 */
    {
      *fp = *ap;
      *gp = *bp = *cp = (udigit) 0;
   }

  B[0] = (udigit) 1;

  for(i=anzBI, bp=B+anzBI, cp=C+anzBI, fp=F+anzBI, gp=G+anzBI; 
      i<2*anzBI; i++, bp++, cp++, fp++, gp++)
    {
      *fp = *gp = *cp = *bp = (udigit) 0;
    }

  G[0] = (udigit) 1;  
  i = gf2n::degree;
  G[i/(8*sizeof(udigit))] ^= udigit (1 << (i % (8*sizeof(udigit))));
  G[gf2n::exp1/(8*sizeof(udigit))] ^= udigit (1 << (gf2n::exp1 % (8*sizeof(udigit))));
  G[gf2n::exp2/(8*sizeof(udigit))] ^= udigit (1 << (gf2n::exp2 % (8*sizeof(udigit))));
  G[gf2n::exp3/(8*sizeof(udigit))] ^= udigit (1 << (gf2n::exp3 % (8*sizeof(udigit))));

  Clast = Blast  = 0;        /* initialize last non zero indices */
  Flast = Glast = anzBI-1;

  fp = F+Flast;
  while(*fp == (udigit) 0 && Flast > 0)
    {
      Flast --; fp--;
    }
    
  /*************************************************************
    start with gcd-computation, use the invariant:

                 B * F + ?? * m = F 
                 C * F + ?? * m = G

   ****************************************************************/


                         /* make F odd, if necessary */
  h = F[0];
  fp = F;

  if (!(h & 1))
    {
      while(h == (udigit) 0)
        {
          fp++;
          xdegree  += 8*sizeof(udigit);
          h = *fp;
        }
      
      while (!(h & 1))
        {
          xdegree++;
          h >>= 1;
        }
      shift_right(F, xdegree, Flast);

      if (Flast == 0 && F[0] == 1)
        goto final_step;
    }

  

  while (1) 
    {                               /* degree(G) > degree(F)*/
      do
        { 
          j = (unsigned int) max(Clast, Blast);
          for (i=0, cp = C, bp = B; i<=j; i++, cp++, bp++)   /* C = C+B */
            (*cp) ^= (*bp);
          
          Clast = j; 
          cp--;
          while(*cp == (udigit) 0 && Clast > 0)
            {
              Clast -- ; 
              cp--;
            }
          
          h = G[0] ^ F[0];
          i = 0; 
     
          while(h == (udigit) 0)
            {
              xdegree += 8*sizeof(udigit);
              i += 8*sizeof(udigit);
              h = G[i/(8*sizeof(udigit))] ^ F[i/(8*sizeof(udigit))];
            }
          
          while (!(h & 1))
            {
              xdegree++;
              i++;
              h >>= 1;
            }
          add_shift_right(G, F, i, Glast, Flast);
          shift_left(B, i, Blast);
          
          if (Glast == 0 && G[0] == (udigit) 1)          /* G == 1  ?? */
            goto final_step;
        }
      while (Glast > Flast || ((G[Glast] >= F[Flast]) && (Glast == Flast)));

      do                                     /* deg F > deg G  */
        {
          j = (unsigned int) max(Clast, Blast);
          for (i=0, bp = B, cp = C; i<=j; i++, bp++, cp++)   /* B = B + C */
            (*bp) ^= (*cp);

          Blast = j; 
       bp--;
          while (*bp == 0 && Blast > 0)
            {
              Blast --;
              bp--;
            }
                                      
          i = 0;                       /* make F odd */
          h = F[0] ^ G[0];
          
          while(h == (udigit) 0)
            {
              xdegree += 8*sizeof(udigit);
              i += 8*sizeof(udigit);
              h = F[i/(8*sizeof(udigit))] ^ G[i/(8*sizeof(udigit))];
            }
          
          while (!(h & 1))
            {
              xdegree++;
              i ++;
              h >>= 1;
            }
          add_shift_right(F, G, i, Flast, Glast);
          shift_left(C, i, Clast);
          
          if (Flast == 0 && F[0] == (udigit) 1)   /* F == 1 */
           goto final_step;
        } 
      while(Flast > Glast || ((F[Flast] >= G[Glast]) && (Flast == Glast)));
    }
 

final_step:       /* check whether F == 1 mod modulus or G == 1 mod modulus */

  if (G[0] == (udigit) 1 && Glast == 0)
    { 
      delete[] B;

      B = C;
      Blast = Clast;
    }
  /* now B == x^(xdegree) mod modulus and B has last index Blast */

  while (xdegree >= 8*sizeof(udigit))
    {
      xdegree -= 8*sizeof(udigit);
      h = B[0];
      
      B[0] = 0;
      w = gf2n::degree / (8*sizeof(udigit));
      l = gf2n::degree % (8*sizeof(udigit));
      Blast = w;
      
      if (l != 0)
        {
          B[w] ^= (h << l);
          B[w+1] ^= (h >> (8*sizeof(udigit)-l));
          Blast++;
        }
      else 
        B[w] ^= h;
      
      w = gf2n::exp1 / (8*sizeof(udigit));
      l = gf2n::exp1 % (8*sizeof(udigit));
      
      if (l != 0)
        {
          B[w] ^= (h << l);
          B[w+1] ^= (h >> (8*sizeof(udigit)-l));
      }
      else
        B[w] ^= h;
         
      w = gf2n::exp2 / (8*sizeof(udigit));
      l = gf2n::exp2 % (8*sizeof(udigit));
      
      if (l != 0)
        {
          B[w] ^= (h << l);
          B[w+1] ^= (h >> (8*sizeof(udigit)-l));
      }
      else
        B[w] ^= h;
        
      w = gf2n::exp3 / (8*sizeof(udigit));
      l = gf2n::exp3 % (8*sizeof(udigit));
      
      if (l != 0)
        {
          B[w] ^= (h << l);
          B[w+1] ^= (h >> (8*sizeof(udigit)-l));
      }
      else
        B[w] ^= h;
      
      shift_right(B, 8*sizeof(udigit), Blast); 
    }
  
  h = B[0] & ((1<<xdegree)-1);     
  B[0] ^= h;
  w = gf2n::degree / (8*sizeof(udigit));
  l = gf2n::degree % (8*sizeof(udigit));
  Blast = w;
  if (l != 0)
    {
      B[w] ^= (h << l);
      B[w+1] ^= (h >> (8*sizeof(udigit)-l));
      Blast++;
    }
  else B[w] ^= h;
  
  w = gf2n::exp1 / (8*sizeof(udigit));
  l = gf2n::exp1 % (8*sizeof(udigit));
              
  if (l != 0)
    {
      B[w] ^= (h << l);
      B[w+1] ^= (h >> (8*sizeof(udigit)-l));
    }
  else B[w] ^= h;

  w = gf2n::exp2 / (8*sizeof(udigit));
  l = gf2n::exp2 % (8*sizeof(udigit));
              
  if (l != 0)
    {
      B[w] ^= (h << l);
      B[w+1] ^= (h >> (8*sizeof(udigit)-l));
    }
  else B[w] ^= h;
  
  w = gf2n::exp3 / (8*sizeof(udigit));
  l = gf2n::exp3 % (8*sizeof(udigit));
              
  if (l != 0)
    {
      B[w] ^= (h << l);
      B[w+1] ^= (h >> (8*sizeof(udigit)-l));
    }
  else B[w] ^= h;
  
  shift_right(B, xdegree, Blast);

  for (i=0, bp=B, cp=res; i<=Blast; i++, bp++, cp++)  /* copy B into res */
    *cp= *bp;
     
  if (Blast < anzBI-1)
    for(i=Blast+1; i<anzBI; i++, cp++)
      *cp = (udigit) 0;
  
  delete[] B;
  delete[] F;
  delete[] G;
}

/**********************************************************************/

void general_invert(udigit *res, udigit *a)
{
  register udigit h;
  register udigit *bp, *cp, *fp, *gp, *ap;
  unsigned int i, j, w, l, s;
  unsigned int xdegree=0;
  udigit *B, *C, *F, *G;
  unsigned int anzBI = gf2n::anzBI;
  unsigned int Clast, Blast, Flast, Glast;

  partial_reduce2[gf2n::invsel](a); /* make sure that a is totally reduced  */

  B = bp = new udigit[2*anzBI];  /* allocate memory for all array variables */
  C = cp = new udigit[2*anzBI];
  F = fp = new udigit[2*anzBI];
  G = gp = new udigit[2*anzBI];

  if (( B == NULL) || (C == NULL) || (F==NULL) || (G==NULL))
    lidia_error_handler("gf2n","invert()::can't allocate memory");
  
  for (i=0, ap=a; i < anzBI; i++, fp++, gp++, cp++, bp++, ap++)  
                /* initialize B=1, C = 0, F = a, G = modulus, res = 0 */
    {
      *fp = *ap;
      *gp = *bp = *cp = (udigit) 0;
    }

  B[0] = (udigit) 1;

  for(i=anzBI, bp=B+anzBI, cp=C+anzBI, fp=F+anzBI, gp=G+anzBI; 
      i<2*anzBI; i++, bp++, cp++, fp++, gp++)
    {
      *fp = *gp = *cp = *bp = (udigit) 0;
    }

  G[0] = (udigit) 1;  
  i = gf2n::degree;
  G[i/(8*sizeof(udigit))] ^= udigit (1 << (i % (8*sizeof(udigit))));
  
  for (j=0; j < gf2n::anz_exponents; j++)
    {
      i = gf2n::exponents[j];
      G[i/(8*sizeof(udigit))] ^= udigit (1 << (i % (8*sizeof(udigit))));
    }

  Clast = Blast  = 0;        /* initialize last non zero indices */
  Flast = Glast = anzBI-1;

  fp = F+Flast;
  while(*fp == (udigit) 0 && Flast > 0)
    {
      Flast --; fp--;
    }
  
  /*************************************************************
    start with gcd-computation, use the invariant:

                 B * F + ?? * m = F 
                 C * F + ?? * m = G

   ****************************************************************/


                         /* make F odd, if necessary */
  h = F[0];
  fp = F;

  if (!(h & 1))
    {
      while(h == (udigit) 0)
        {
          fp++;
          xdegree  += 8*sizeof(udigit);
          h = *fp;
        }
      
      while (!(h & 1))
        {
          xdegree++;
          h >>= 1;
        }
      shift_right(F, xdegree, Flast);

      if (Flast == 0 && F[0] == 1)
        goto final_step;
    }

  while (1) 
    {                               /* degree(G) > degree(F)*/
      do
        { 
          j = (unsigned int) max(Clast, Blast);
          for (i=0, cp = C, bp = B; i<=j; i++, cp++, bp++)   /* C = C+B */
            (*cp) ^= (*bp);
          
          Clast = j; 
          cp--;
          while(*cp == (udigit) 0 && Clast > 0)
            {
              Clast -- ; 
              cp--;
            }

          h = G[0] ^ F[0];
          i = 0; 
          
          while(h == (udigit) 0)
            {
              xdegree += 8*sizeof(udigit);
              i += 8*sizeof(udigit);
              h = G[i/(8*sizeof(udigit))] ^ F[i/(8*sizeof(udigit))];
            }

          while (!(h & 1))
            {
              xdegree++;
              i++;
              h >>= 1;
            }
          add_shift_right(G, F, i, Glast, Flast);
          shift_left(B, i, Blast);
          
          if (Glast == 0 && G[0] == (udigit) 1)          /* G == 1  ?? */
            goto final_step;
        }
      while (Glast > Flast || ((G[Glast] >= F[Flast]) && (Glast == Flast)));

      do                                     /* deg F > deg G  */
        {
          j = (unsigned int) max(Clast, Blast);
          for (i=0, bp = B, cp = C; i<=j; i++, bp++, cp++)   /* B = B + C */
            (*bp) ^= (*cp);

          Blast = j; 
          bp--;
          while (*bp == 0 && Blast > 0)
            {
              Blast --;
              bp--;
            }

          i = 0;                       /* make F odd */
          h = F[0] ^ G[0];
          
          while(h == (udigit) 0)
            {
              xdegree += 8*sizeof(udigit);
              i += 8*sizeof(udigit);
              h = F[i/(8*sizeof(udigit))] ^ G[i/(8*sizeof(udigit))];
            }

          while (!(h & 1))
            {
              xdegree++;
              i ++;
              h >>= 1;
            }
          add_shift_right(F, G, i, Flast, Glast);
          shift_left(C, i, Clast);

          if (Flast == 0 && F[0] == (udigit) 1)   /* F == 1 */
            goto final_step;
        } 
      while(Flast > Glast || ((F[Flast] >= G[Glast]) && (Flast == Glast)));
    }
 

final_step:       /* check whether F == 1 mod modulus or G == 1 mod modulus */


  if (G[0] == (udigit) 1 && Glast == 0)
    { 
      delete[] B;
      
      B = C;
      Blast = Clast;
    }

  /* now B == x^(xdegree) mod modulus and B has last index Blast */

  while (xdegree >= 8*sizeof(udigit))
    {
      xdegree -= 8*sizeof(udigit);
      h = B[0];
      
      while (h != 0)
        {
          B[0] = 0;
          w = gf2n::degree / (8*sizeof(udigit));
          l = gf2n::degree % (8*sizeof(udigit));
          Blast = w;

          if (l != 0)
            {
              B[w] ^= (h << l);
              B[w+1] ^= (h >> (8*sizeof(udigit)-l));
              Blast++;
            }
          else 
            B[w] ^= h;
          
          for (j=0; j<gf2n::anz_exponents; j++) 
            {
              s = gf2n::exponents[j];
              w = s / (8*sizeof(udigit));
              l = s % (8*sizeof(udigit));

               if (l != 0)
                 {
                   B[w] ^= (h << l);
                   B[w+1] ^= (h >> (8*sizeof(udigit)-l));
                 }
               else
                   B[w] ^= h;
            }
          h = B[0];
          while (B[Blast] == 0)
            Blast --;
        }
      shift_right(B, 8*sizeof(udigit), Blast); 
    }

  h = B[0] & ((1<<xdegree)-1);     

  while (h != 0)
        {
          B[0] ^= h;
          w = gf2n::degree / (8*sizeof(udigit));
          l = gf2n::degree % (8*sizeof(udigit));
          Blast = w;
          if (l != 0)
         {
              B[w] ^= (h << l);
              B[w+1] ^= (h >> (8*sizeof(udigit)-l));
              Blast++;
            }
          else B[w] ^= h;
          
          
          for (j=0; j<gf2n::anz_exponents; j++) 
            {
              s = gf2n::exponents[j];
              w = s / (8*sizeof(udigit));
              l = s % (8*sizeof(udigit));
              
              if (l != 0)
                {
                  B[w] ^= (h << l);
                  B[w+1] ^= (h >> (8*sizeof(udigit)-l));
                }
              else B[w] ^= h;
            }
          h = B[0] & ((1<<xdegree)-1); 
          while (B[Blast] == 0 && Blast > 0)
            Blast --;
        }
  shift_right(B, xdegree, Blast);

  for (i=0, bp=B, cp=res; i<=Blast; i++, bp++, cp++)  /* copy B into res */
    *cp= *bp;
    
  if (Blast < anzBI-1)
    for(i=Blast+1; i<anzBI; i++, cp++)
      *cp = (udigit) 0;

  delete[] B;
  delete[] F;
  delete[] G;
}

/******* invert - selector *********************************/

void ( *uinvert[] ) (udigit*, udigit*) =
{
tri_invert,  pent_invert, general_invert
};








