/****************************************************************************
**
*W  vecgf2.c                    GAP source                       Frank Celler
**
**
*Y  Copyright (C)  1997,  Lehrstuhl D für Mathematik,  RWTH Aachen,  Germany
*Y  (C) 1998 School Math and Comp. Sci., University of St Andrews, Scotland
*Y  Copyright (C) 2002 The GAP Group
*/

#include "vecgf2.h"

#include "ariths.h"
#include "blister.h"
#include "bool.h"
#include "error.h"
#include "finfield.h"
#include "gvars.h"
#include "integer.h"
#include "lists.h"
#include "modules.h"
#include "opers.h"
#include "plist.h"
#include "precord.h"
#include "range.h"
#include "records.h"
#include "stats.h"
#include "vec8bit.h"

#include <gmp.h>


/****************************************************************************
**
*F * * * * * * * * * * * imported library variables * * * * * * * * * * * * *
*/


/****************************************************************************
**
*V  TYPE_LIST_GF2VEC  . . . . . . . . . . . . . . type of a GF2 vector object
*/
Obj TYPE_LIST_GF2VEC;


/****************************************************************************
**
*V  TYPE_LIST_GF2VEC_LOCKED. . . .  type of a mutable GF2 vector object
**                                          with locked representation
*/
Obj TYPE_LIST_GF2VEC_LOCKED;


/****************************************************************************
**
*V  TYPE_LIST_GF2VEC_IMM  . . . . . .  type of an immutable GF2 vector object
*/
Obj TYPE_LIST_GF2VEC_IMM;

/****************************************************************************
**
*V  TYPE_LIST_GF2VEC_IMM_LOCKED. . . .  type of an immutable GF2 vector object
**                                          with locked representation
*/
Obj TYPE_LIST_GF2VEC_IMM_LOCKED;


/****************************************************************************
**
*V  TYPE_LIST_GF2MAT  . . . . . . . . . . . . . . type of a GF2 matrix object
*/
Obj TYPE_LIST_GF2MAT;


/****************************************************************************
**
*V  TYPE_LIST_GF2MAT_IMM  . . . . . .  type of an immutable GF2 matrix object
*/
Obj TYPE_LIST_GF2MAT_IMM;


/****************************************************************************
**
*V  IsGF2VectorRep  . . . . . . . . . . . . . . . . . . . . . . . . .  filter
*/
Obj IsGF2VectorRep;


/****************************************************************************
**
*V  GF2One  . . . . . . . . . . . . . . . . . . . . . . . . . . .  one of GF2
*/
Obj GF2One;


/****************************************************************************
**
*V  GF2Zero . . . . . . . . . . . . . . . . . . . . . . . . . . . zero of GF2
*/
Obj GF2Zero;


/****************************************************************************
**
*F * * * * * * * * * * * * arithmetic operations  * * * * * * * * * * * * * *
*/

static inline void AddGF2VecToGF2Vec(
  UInt *	ptS,
  UInt *	ptV,
  UInt		len)
{
  register UInt ct;
  ct = (len+BIPEB-1)/BIPEB;
  while ( ct-- ) {
    *ptS++ ^= *ptV++;
  }
}

/****************************************************************************
**
*F  AddCoeffsGF2VecGF2Vec( <sum>, <vec> ) . . . . . . . .  add <vec> to <sum>
**
**  `AddCoeffsGF2VecGF2Vec' adds  the   entries of <vec>  to <sum>.    If the
**  length  are not equal the  missing entries are  assumed  to be zero.  The
**  position of the rightmost Z(2) is returned.
*/

UInt RightMostOneGF2Vec (
    Obj                 vec )
{
    UInt                len;

    len = LEN_GF2VEC(vec);
    while ( 0 < len ) {
        if ( BLOCK_ELM_GF2VEC(vec,len) == 0 )
	    len = BIPEB*((len-1)/BIPEB);
        else if ( BLOCK_ELM_GF2VEC(vec,len) & MASK_POS_GF2VEC(len) )
            break;
	else
	  len--;
    }
    return len;
}


Obj AddCoeffsGF2VecGF2Vec (
    Obj                 sum,
    Obj                 vec )
{
    UInt *              ptS;
    UInt *              ptV;
    UInt                len;

    /* get the length                                                      */
    len = LEN_GF2VEC(vec);
    
    /* grow <sum> is necessary                                             */
    if ( LEN_GF2VEC(sum) < len ) {
        ResizeWordSizedBag( sum, SIZE_PLEN_GF2VEC(len) );
        SET_LEN_GF2VEC( sum, len );
    }

    /* add <vec> to <sum>                                                  */
    ptS = BLOCKS_GF2VEC(sum);
    ptV = BLOCKS_GF2VEC(vec);
    AddGF2VecToGF2Vec(ptS, ptV, len);
    return INTOBJ_INT(RightMostOneGF2Vec(sum));
}


static inline void
CopySection_GF2Vecs(Obj src, Obj dest, UInt smin, UInt dmin, UInt nelts)
{
    UInt   soff;
    UInt   doff;
    UInt * sptr;
    UInt * dptr;

    /* switch to zero-based indices and find the first blocks and so on */
    soff = (smin - 1) % BIPEB;
    doff = (dmin - 1) % BIPEB;
    sptr = BLOCKS_GF2VEC(src) + (smin - 1) / BIPEB;
    dptr = BLOCKS_GF2VEC(dest) + (dmin - 1) / BIPEB;

    CopyBits(sptr, soff, dptr, doff, nelts);
    return;
}

/****************************************************************************
**
*F  AddPartialGF2VecGF2Vec( <sum>, <vl>, <vr>, <n> )  . . . . . . partial sum
**
**  'AddPartialGF2VecGF2Vec' adds the entries  of <vl> and <vr> starting from
**  that block which is corresponding to the entry with number <n> and stores
**  the result in <sum>.
**
**  Note: The other entries are set to be zero. So use a higher value for <n>
**        only for vectors, which both have leading zero-entries. 
**
**  You  can use  the parameter  <n> for  example for  an  gauss-algorithm on 
**  gf2-matrices  can be  improved,  because when  using the gauss-algorithm,
**  you  know that  the leading entries of two vectors to be  added are equal
**  zero. If <n> = 1 all entries are added. 
**
**  Note that the caller has to ensure, that <sum> is a gf2-vector with the 
**  correct size.
*/
Obj AddPartialGF2VecGF2Vec (
    Obj                 sum,
    Obj                 vl,
    Obj                 vr,
    UInt                n )
{
    UInt *              ptL;            /* bit field of <vl>               */
    UInt *              ptR;            /* bit field of <vr>               */
    UInt *              ptS;            /* bit field of <sum>              */
    UInt *              end;            /* end marker                      */
    UInt                len;            /* length of the list              */
    UInt                offset;         /* number of block to start adding */
    UInt                x;
    

    /* both operands lie in the same field                                 */
    len = LEN_GF2VEC(vl);
    if ( len != LEN_GF2VEC(vr) ) {
        ErrorMayQuit( "Vector +: vectors must have the same length",
                   0L, 0L );
        return 0;
    }


    /* calculate the offset for adding                                     */
    if ( n == 1 ) {  
        ptL = BLOCKS_GF2VEC(vl);
        ptR = BLOCKS_GF2VEC(vr);
        ptS = BLOCKS_GF2VEC(sum);
        end = ptS + ((len+BIPEB-1)/BIPEB);
    } else {
        offset = ( n - 1 ) / BIPEB;
        ptL = BLOCKS_GF2VEC(vl) + offset ;
        ptR = BLOCKS_GF2VEC(vr) + offset ;
        ptS = BLOCKS_GF2VEC(sum) + offset ;
        end = ptS + ((len+BIPEB-1)/BIPEB) - offset;
    }
    
    /* loop over the entries and add                                       */
    if (vl == sum)
      while ( ptS < end )
	{
	  /* maybe remove this condition */
	  if ((x = *ptR)!= 0)
	    *ptS = *ptL ^ x;
	  ptL++; ptS++; ptR++;
	}
    else if (vr == sum)
      while ( ptS < end )
	{
	  /* maybe remove this condition */
	  if ((x = *ptL) != 0)
	    *ptS = *ptR ^ x;
	  ptL++; ptS++; ptR++;
	}
    else
      while (ptS < end )
	*ptS++ = *ptL++ ^ *ptR++;

    /* return the result                                                   */
    return sum;
}


/****************************************************************************
**
*F  ProdGF2VecGF2Vec( <vl>, <vr> )  . . . . . . .  product of two GF2 vectors
**
**  'ProdVecGF2VecGF2' returns  the product of  the two GF2 vectors <vl> and
**  <vr>.   The product is  the folded sum of   the corresponding entries of
**  <vl> and <vr>.
**
**  'ProdVecGF2VecGF2' is an improved  version of the general multiplication,
**  which  does not  call 'PROD'  but uses bit  operations instead.   It will
**  always return either 'GF2One' or 'GF2Zero'.
*/
#ifdef SYS_IS_64_BIT

#define PARITY_BLOCK(m) \
  do { m = m ^ (m>>32); \
       m = m ^ (m>>16); \
       m = m ^ (m>>8);  \
       m = m ^ (m>>4);  \
       m = m ^ (m>>2);  \
       m = m ^ (m>>1);  \
  } while(0)

#else

#define PARITY_BLOCK(m) \
  do { m = m ^ (m>>16); \
       m = m ^ (m>>8);  \
       m = m ^ (m>>4);  \
       m = m ^ (m>>2);  \
       m = m ^ (m>>1);  \
  } while(0)

#endif

Obj ProdGF2VecGF2Vec ( Obj vl, Obj vr )
{
    UInt *              ptL;            /* bit field of <vl>               */
    UInt *              ptR;            /* bit field of <vr>               */
    UInt                lenL;           /* length of the list              */
    UInt                lenR;           /* length of the list              */
    UInt                len;            /* minimum of the lengths          */
    UInt                nrb;            /* number of whole blocks to use   */
    UInt                m;              /* number of bits in a block       */
    UInt                n;              /* number of bits in blist         */
    UInt                i;              /* loop variable                   */
    UInt                mask;           /* bit selecting mask              */

    /* both operands lie in the same field                                 */
    lenL = LEN_GF2VEC(vl);
    lenR = LEN_GF2VEC(vr);
    len = (lenL < lenR) ? lenL : lenR;
    
    if ( len == 0 ) {
      ErrorMayQuit(
        "Vector *: both vectors must have at least one entry",
        (Int)0, (Int)0 );
      return 0;
    }

    /* loop over the entries and multiply                                  */
    ptL = BLOCKS_GF2VEC(vl);
    ptR = BLOCKS_GF2VEC(vr);
    nrb = len /BIPEB;
    n   = 0;
    for ( i = nrb;  i > 0;  i-- ) {
        m = (*ptL++) & (*ptR++);
        PARITY_BLOCK(m);
        n ^= m;
    }
    /* now process the remaining bits */

    mask = 1;
    for (i = 0; i < len % BIPEB; i++)
      {
	n ^= (mask & *ptL & *ptR) >> i;
	mask <<= 1;
      }

    /* return the result                                                   */
    return (n & 1) ? GF2One : GF2Zero;
}


/****************************************************************************
**
*F  ProdGF2VecGF2Mat( <vl>, <vr> )  . .  product of GF2 vector and GF2 matrix
**
**  'ProdGF2VecGF2Mat'  returns the product  of the  GF2 vector <vl>  and the
**  GF2 matrix  <vr>.   The product is   the sum of  the  rows of <vr>,  each
**  multiplied by the corresponding entry of <vl>.  Note that the  caller has
**  to ensure, that <vl> is a gf2-vector and <vr> is a gf2-matrix.
*/
Obj ProdGF2VecGF2Mat ( Obj vl, Obj vr )
{
    UInt                len;            /* length of the list              */
    UInt                stop;
    UInt                col;            /* length of the rows              */
    UInt                i;              /* loop variables                  */
    Obj                 prod;           /* product, result                 */
    Obj                 row1;           /* top row of matrix               */
    UInt *              start;
    UInt *              ptL;
    UInt                mask;
    
    /* both operands lie in the same field                                 */
    len = LEN_GF2VEC(vl);
    if (len > LEN_GF2MAT(vr))
      len = LEN_GF2MAT(vr);
    
    /* make the result vector                                              */
    row1 = ELM_GF2MAT( vr, 1 );
    col = LEN_GF2VEC( row1 );
    NEW_GF2VEC( prod, (IS_MUTABLE_OBJ(vl) || IS_MUTABLE_OBJ(row1)) ? 
		TYPE_LIST_GF2VEC : TYPE_LIST_GF2VEC_IMM, col );
    
    /* get the start and end block                                         */
    start = BLOCKS_GF2VEC(prod);
    ptL   = BLOCKS_GF2VEC(vl);

    /* loop over the vector                                                */
    for ( i = 1;  i <= len;  ptL++ )  {

        /* if the whole block is zero, get the next entry                  */
        if (*ptL == 0) {
            i += BIPEB;
            continue;
        }
        
        /* run through the block                                           */
        stop = i + BIPEB - 1;
        if ( len < stop )
            stop = len;
        for ( mask = 1;  i <= stop;  i++, mask <<= 1 ) {

            /* if there is entry add the row to the result                 */
            if ( (*ptL & mask) != 0 ) {
                UInt * ptRR = BLOCKS_GF2VEC(ELM_GF2MAT(vr,i));
		AddGF2VecToGF2Vec(start, ptRR, col);
            }
        }
    }

    /* return the result                                                   */
    return prod;
}


/****************************************************************************
**
*F  ProdGF2MatGF2Vec( <ml>, <vr> )  . .  product of GF2 matrix and GF2 vector
**
**  'ProdGF2MatGF2Vec'  returns the product  of the  GF2 matrix <ml>  and the
**  GF2 vector  <vr>.   The ith entry of the
**  product is the inner product of  the  ith row of <ml> with <vr>.
**  Note that the  caller has
**  to ensure, that <ml> is a GF2 matrix and <vr> is a GF2 vector.
*/
Obj ProdGF2MatGF2Vec ( Obj ml, Obj vr )
{
    UInt                len;            /* length of the vector            */
    UInt                ln1;            /* length of the rows of the mx    */
    UInt                ln2;            /* length of the matrix            */
    UInt *              ptL;            /* bit field of <ml>[j]            */
    UInt *              ptR;            /* bit field of <vr>               */
    UInt                nrb;            /* number of blocks in blist       */
    UInt                m;              /* number of bits in a block       */
    UInt                n;              /* number of bits in blist         */
    UInt                i;              /* loop variable                   */
    UInt                j;              /* loop variable                   */
    Obj                 prod;           /* result                          */
    UInt                mask;           /* a one bit mask */
    
    /* both operands lie in the same field                                 */
    len = LEN_GF2VEC(vr);
    ln2 = LEN_GF2MAT(ml);
    if ( 0 == ln2 ) {
      ErrorMayQuit("PROD: empty GF2 matrix * GF2 vector not allowed",0,0);
    }

    ln1 = LEN_GF2VEC(ELM_GF2MAT(ml,1));
    if ( len > ln1 ) {
      len = ln1;
    }

    /* make the result vector                                              */
    NEW_GF2VEC( prod, (IS_MUTABLE_OBJ(ELM_GF2MAT(ml,1)) || IS_MUTABLE_OBJ(vr)) ? 
		TYPE_LIST_GF2VEC :TYPE_LIST_GF2VEC_IMM, ln2 );

    /* loop over the entries and multiply                                  */
    nrb = len/BIPEB;
    for ( j = 1;  j <= ln2;  j++ ) {
        ptL = BLOCKS_GF2VEC(ELM_GF2MAT(ml,j));
        ptR = BLOCKS_GF2VEC(vr);
        n   = 0;
        for ( i = 1;  i <= nrb;  i++ ) {
            m = (*ptL++) & (*ptR++);
            PARITY_BLOCK(m);
            n ^= m;
        }

	mask = 1;
	for (i = 0; i < len % BIPEB; i++)
	  {
	    n ^= (mask & *ptL & *ptR) >> i;
	    mask <<= 1;
	  }

	
        if ( n & 1 )
            BLOCK_ELM_GF2VEC(prod,j) |= MASK_POS_GF2VEC(j);
    }

    /* return the result                                                   */
    return prod;
}

/****************************************************************************
**
*F  ProdGF2MatGF2MatSimple( <ml>, <mr> )  . .  product of GF2 matrix and GF2 matrix
*F  ProdGF2MatGF2MatAdvanced( <ml>, <mr>, <greaselevel>, <blocksize> )
**                                     . .  product of GF2 matrix and GF2 matrix
**
**  'ProdGF2MatGF2MatSimple'  returns the product  of the  GF2 matrix <ml>  and the
**  GF2 matrix  <mr>.  This simply calls ProdGF2VecGF2Mat once on each row.
**
** ProdGF2MatGF2MatAdvanced uses the specified grease and blocking to accelerate
** larger matrix multiplies. In this case, the matrix dimensions must be compatible.
*/

Obj ProdGF2MatGF2MatSimple( Obj ml, Obj mr )
{
  Obj prod;
  UInt i;
  UInt len;
  Obj row;
  Obj rtype;
  len = LEN_GF2MAT(ml);
  prod = NewBag(T_POSOBJ, SIZE_PLEN_GF2MAT(len));
  SET_LEN_GF2MAT(prod,len);
  if (IS_MUTABLE_OBJ(ml) || IS_MUTABLE_OBJ(mr))
    {
      SET_TYPE_POSOBJ(prod, TYPE_LIST_GF2MAT);
      if (IS_MUTABLE_OBJ(ELM_GF2MAT(ml,1)) || IS_MUTABLE_OBJ(ELM_GF2MAT(mr,1)))
	rtype = TYPE_LIST_GF2VEC_LOCKED;
      else
	rtype = TYPE_LIST_GF2VEC_IMM_LOCKED;
    }
  else
    {
      SET_TYPE_POSOBJ(prod, TYPE_LIST_GF2MAT_IMM);
      rtype = TYPE_LIST_GF2VEC_IMM_LOCKED;
    }
  for (i = 1; i <= len; i++)
    {
      row = ProdGF2VecGF2Mat(ELM_GF2MAT(ml,i),mr);

      /* Since I'm going to put this vector into a matrix, I must lock its
	 representation, so that it doesn't get rewritten over GF(2^k) */
      SetTypeDatObj(row, rtype);
      SET_ELM_GF2MAT(prod,i,row);
      CHANGED_BAG(prod);
      TakeInterrupt();
    }
  return prod;
}


/* Utility functions for the advanced matrix multiply code below */


/* extract nbits bits starting from position from in vector vptr
   return them as the nbits least significant bits in a UInt.
   Bits are always numbered least-significant first */

static inline UInt getbits(UInt * vptr, UInt from, UInt nbits)
{
  UInt wno = (from  -1)/BIPEB;
  UInt word1 = vptr[wno];
  UInt shift1 = (from -1)% BIPEB;
  UInt lbit = shift1 + nbits;
  UInt word2;
  if (lbit <= BIPEB)
    {
      /* range is all in one word */
      word1 <<= BIPEB -lbit;
      word1 >>= BIPEB - nbits;
    }
  else
    {
      /* range is split across two words */
      word1 >>= shift1;
      lbit -= BIPEB;
      word2 = vptr[wno+1];
      word2 <<= BIPEB-lbit;
      word2 >>= shift1-lbit;
      word1 |= word2;
    }
  return word1;
}

/* To avoid having a lot of arguments to the recursive getgreasedata function,
   we put the things that don't change in the recursive call into this structure */

struct greaseinfo {
  UInt *pgtags, *pgbuf,  nblocks, *pgrules;
  UInt **prrows;
};



/* Make if necessary the grease row for bits
   controlled by the data in g. Recursive
   so can't be inlined */

static UInt * getgreasedata( struct greaseinfo *g, UInt bits)
{ 
  UInt x,y;
  register UInt *ps, *pd, *ps2,i ;
  UInt *pd1;
  switch(g->pgtags[bits])
    {
    case 0:
      /* Need to make the row */
      x = g->pgrules[bits];
      y = bits ^ (1 << x);
      /* make it by adding row x to grease vector indexed y */
      ps =g->prrows[x];
      ps2 = getgreasedata(g,y);
      pd1 = g->pgbuf + (bits-3)*g->nblocks;
      pd = pd1;
      /* time critical inner loop */
      for (i = g->nblocks; i > 0; i--)
	*pd++ = *ps++ ^ *ps2++;
      /* record that we made it */
      g->pgtags[bits] = 1;
      return pd1;

    case 1:
      /* we've made this one already, so just return it */
      return  g->pgbuf + (bits-3)*g->nblocks;

    case 2:
      /* This one does not need making, bits actually
	 has just a single 1 bit in it */
      return g->prrows[g->pgrules[bits]];

    }
  return (UInt *)0;		/* can't actually get here
				 include the return to pacify compiler */
}



Obj ProdGF2MatGF2MatAdvanced( Obj ml, Obj mr, UInt greasesize , UInt blocksize)
{
  Obj prod;			/* Product Matrix */
  UInt i,j, k, b;		/* Loop counters */
  UInt gs;			/* Actual level of grease for current block */
  UInt *rptr;			/* Pointer to current row of ml */
  UInt bits;			/* current chunk of current row, for lookup in grease tables */
  UInt *v;			/* pointer to computed grease vector */
  UInt len, rlen, ilen;		/* len = length of ml, ilen = row length of ml = length of mr, rlen = row length of mr */
  Obj row;			/* current row of ml, or row of prod when it is being built */
  Obj rtype;			/* type of rows of prod */
  Obj gbuf = (Obj)0;		/* grease buffer */
  Obj gtags = (Obj) 0;		/* grease tags (whether that row is known yet */
  Obj grules = (Obj) 0;		/* rules for making new grease vectors */
  UInt *pgrules;		/* pointer to contents of grules*/
  UInt *pgtags = (UInt *)0;	/* pointer to contents of gtags */
  UInt *pgbuf = (UInt *)0;	/* pointer to grease buffer */
  UInt nwords;			/* number of words in a row of mr */
  UInt glen;			/* 1 << greasesize */
  UInt bs;			/* actual size of current block */
  UInt *pprow;			/* pointer into current row of prod */
  Obj lrowptrs;			/* cache of direct pointers to rows of ml */
  UInt **plrows;		/* and a direct pointer to that cache */
  Obj rrowptrs;			/* and for mr */
  UInt **prrows;
  Obj prowptrs;			/* and for prod */
  UInt **pprows;
  struct greaseinfo g;
  
  len = LEN_GF2MAT(ml);
  row = ELM_GF2MAT(mr, 1);
  rlen = LEN_GF2VEC(row);
  ilen = LEN_GF2MAT(mr);
  nwords = NUMBER_BLOCKS_GF2VEC(row);
  
  /* Make a zero product matrix */
  prod = NewBag(T_POSOBJ, SIZE_PLEN_GF2MAT(len));
  SET_LEN_GF2MAT(prod,len);
  if (IS_MUTABLE_OBJ(ml) || IS_MUTABLE_OBJ(mr))
    {
      SET_TYPE_POSOBJ(prod, TYPE_LIST_GF2MAT);
      if (IS_MUTABLE_OBJ(ELM_GF2MAT(ml,1)) || IS_MUTABLE_OBJ(ELM_GF2MAT(mr,1)))
	rtype = TYPE_LIST_GF2VEC_LOCKED;
      else
	rtype = TYPE_LIST_GF2VEC_IMM_LOCKED;
    }
  else
    {
      SET_TYPE_POSOBJ(prod, TYPE_LIST_GF2MAT_IMM);
      rtype = TYPE_LIST_GF2VEC_IMM_LOCKED;
    }


  for (i = 1; i <= len; i++)
    {
      NEW_GF2VEC(row, rtype, rlen);
      SET_ELM_GF2MAT(prod,i,row);
      CHANGED_BAG(prod);
    }

  /* Cap greasesize and blocksize by the actual length  */
  if (ilen < greasesize)
    greasesize = ilen;
  if (ilen < greasesize*blocksize)
    blocksize = (ilen + greasesize-1)/greasesize;


  /* calculate glen*/
  glen = 1 << greasesize;
  
  /* Allocate memory */

  lrowptrs = NewBag(T_DATOBJ, sizeof(UInt *)*len);
  rrowptrs = NewBag(T_DATOBJ, sizeof(UInt *)*ilen);
  prowptrs = NewBag(T_DATOBJ, sizeof(UInt *)*len);
  
  if (greasesize >= 2)
    {
      gbuf = NewBag(T_DATOBJ, sizeof(UInt)*nwords*(glen-3)*blocksize);
      gtags = NewBag(T_DATOBJ, sizeof(UInt)* glen*blocksize);
      grules = NewBag(T_DATOBJ, sizeof(Int)* glen);
      
      
      /* From here no garbage collections */
      
      pgtags = (UInt *)ADDR_OBJ(gtags);
      pgrules = (UInt *)ADDR_OBJ(grules);
      pgbuf = (UInt *)ADDR_OBJ(gbuf);
      

      /* Calculate the greasing rules */
      for (j = 3; j < glen; j++)
	for (i = 0; i < greasesize; i++)
	  if ((j & (1 << i)) != 0)
	    {
	      pgrules[j] = i;
	      break;
	    }
      for (j = 0; j < greasesize; j++)
	pgrules[1<<j] = j;

      /* fill in some more bits of g */
      g.pgrules = pgrules;
      g.nblocks = nwords;
    }

  /* Take direct pointers to all the parts of all the matrices to avoid multiple
     indirection overheads */
  plrows = (UInt **)ADDR_OBJ(lrowptrs);
  prrows = (UInt **)ADDR_OBJ(rrowptrs);
  pprows = (UInt **)ADDR_OBJ(prowptrs);

  for (i = 0; i < len; i++)
    {
      plrows[i] = BLOCKS_GF2VEC(ELM_GF2MAT(ml,i+1));
      pprows[i] = BLOCKS_GF2VEC(ELM_GF2MAT(prod, i+1));
    }
  for (i = 0; i < ilen; i++)
    prrows[i] = BLOCKS_GF2VEC(ELM_GF2MAT(mr, i+1));


  /* OK, finally ready to start work */
  /* loop over blocks */
  for (b = 1; b <= ilen; b += blocksize*greasesize)
    {
      /* last block may be a small one */
      bs = blocksize;
      if ((b + bs*greasesize) > ilen)
	bs = (ilen - b + greasesize)/greasesize;

      /* If we're greasing, start afresh */
      if (greasesize > 1)
	{
	  for (k = 0; k < bs; k++)
	    {
	      for (j = 0; j < 1 << greasesize; j++)
		pgtags[k*glen+j] = 0;
	      /* powers of 2 correspond to rows of mr */
	      for (j =0; j < greasesize; j++)
		pgtags[k*glen+ (1<<j)] = 2;
	    }
	}
      
      /* For each block, we run through rows of ml & prod */
      for (j = 1; j <= len; j++)
	{
	  /* get pointers */
	  rptr = plrows[j-1];
	  pprow = pprows[j-1];

	  /* Now within the block, we have multiple grease-units, run through them */
	  for (i = 0; i < bs; i++)
	    {
	      /* start of current grease unit */	     
	      k = b + i*greasesize;

	      /* last unit of last block may be short */
	      gs = greasesize;
	      if (k+gs > ilen)
		gs = ilen - k +1;

	      /* find the appropriate parts of grease tags
		 grease buffer and mr. Store in g */
	      
	      if (gs > 1)
		{
		  g.pgtags = pgtags + glen*i;
		  g.pgbuf = pgbuf + (glen-3)*nwords*i;
		  g.prrows = prrows + k -1;
		}

	      /* get a chunk from a row of ml */
	      bits = getbits(rptr, k, gs);

	      /* 0 means nothing to do */
	      if (bits == 0)
		continue;
	      else if (bits == 1) /* handle this one specially to speed up the greaselevel 1 case */
		v = prrows[k-1]; /* -1 is because k is 1-based index */
	      else
		v = getgreasedata(&g,bits); /* The main case */
				/* This function should be inlined */
	      AddGF2VecToGF2Vec(pprow, v,  rlen);  
	    }  
	  }
	
      /* Allow GAP to respond to Ctrl-C */
      if (TakeInterrupt()) {
	/* Might have been a garbage collection, reload everything */
	if (greasesize >= 2) {
	  pgtags = (UInt *)ADDR_OBJ(gtags);
	  pgrules = (UInt *)ADDR_OBJ(grules);
	  pgbuf = (UInt *)ADDR_OBJ(gbuf);
	  /* fill in some more bits of g */
	  g.pgrules = pgrules;
	  g.nblocks = nwords;
	}
	plrows = (UInt **)ADDR_OBJ(lrowptrs);
	prrows = (UInt **)ADDR_OBJ(rrowptrs);
	pprows = (UInt **)ADDR_OBJ(prowptrs);
	for (i = 0; i < len; i++)
	  {
	    plrows[i] = BLOCKS_GF2VEC(ELM_GF2MAT(ml,i+1));
	    pprows[i] = BLOCKS_GF2VEC(ELM_GF2MAT(prod, i+1));
	  }
	for (i = 0; i < ilen; i++)
	  prrows[i] = BLOCKS_GF2VEC(ELM_GF2MAT(mr, i+1));
      }
    }
  return prod;
}

/****************************************************************************
**
*F  FuncPROD_GF2VEC_ANYMAT( <self>, <v>, <m>)
**
**  method to handle vector*plain list of GF2Vectors reasonably efficiently.
*/
Obj FuncPROD_GF2VEC_ANYMAT(Obj self, Obj vec, Obj mat)
{
  Obj res;
  UInt len;
  UInt len1;
  Obj row1;
  UInt i;
  UInt block = 0;
  
  len = LEN_GF2VEC(vec);
  if (len > LEN_PLIST(mat))
    len = LEN_PLIST(mat);

  /* Get the first row, to establish the size of the result */
  row1 = ELM_PLIST(mat,1);  
  if (! IS_GF2VEC_REP(row1))
    return TRY_NEXT_METHOD;
  len1 = LEN_GF2VEC(row1);

  /* create the result space */
  NEW_GF2VEC( res,
	      (IS_MUTABLE_OBJ(vec) || IS_MUTABLE_OBJ(row1)) ? TYPE_LIST_GF2VEC : TYPE_LIST_GF2VEC_IMM,
	      len1);

  /* Finally, we start work */
  for (i = 1; i <= len; i++)
    {
      if (i % BIPEB == 1)
	block = BLOCK_ELM_GF2VEC(vec,i);
      if (block & MASK_POS_GF2VEC(i))
     	{
	  row1 = ELM_PLIST(mat,i);  
	  if (! IS_GF2VEC_REP(row1))
	    return TRY_NEXT_METHOD;
	  AddPartialGF2VecGF2Vec(res, res, row1, 1);
	}
    }
  return res;

}

/****************************************************************************
**
*F  InversePlistGF2VecsDesstructive( <list> )
**
**  This is intended to form the core of a method for InverseOp.
**  by this point it should be checked that list is a plain list of GF2 vectors
**  of equal lengths. 
*/
Obj InversePlistGF2VecsDesstructive( Obj list )
{
    UInt                len;            /* dimension                       */
    Obj                 inv;            /* result                          */
    Obj                 row;            /* row vector                      */
    Obj                 old;            /* row from <mat>                  */
    Obj                 tmp;            /* temporary                       */
    UInt *              ptQ;            /* data block of <row>             */
    UInt *              ptP;            /* data block of source row        */
    UInt *              end;            /* end marker                      */
    UInt *              end2;           /* end marker                      */
    UInt                i;              /* loop variable                   */
    UInt                k;              /* loop variable                   */

    len = LEN_PLIST(list);
    
    /* create the identity matrix                                          */
    tmp = NEW_PLIST( T_PLIST, len );
    for ( i = len;  0 < i;  i-- ) {
      NEW_GF2VEC( row, TYPE_LIST_GF2VEC, len );
      BLOCK_ELM_GF2VEC(row,i) |= MASK_POS_GF2VEC(i);
      SET_ELM_PLIST( tmp, i, row );
      CHANGED_BAG(tmp);
    }
    SET_LEN_PLIST(tmp, len);
    inv = tmp;

    /* now start with ( id | mat ) towards ( inv | id )                    */
    for ( k = 1;  k <= len;  k++ ) {

        /* find a nonzero entry in column <k>                              */
        for ( i = k;  i <= len;  i++ ) {
            row = ELM_PLIST( list, i );
            if ( BLOCK_ELM_GF2VEC(row,k) & MASK_POS_GF2VEC(k) )
                break;
        }
        if ( i > len )  {
            return Fail;
        }
        if ( i != k )  {
            row = ELM_PLIST( list, i );
            SET_ELM_PLIST( list, i, ELM_PLIST( list, k ) );
            SET_ELM_PLIST( list, k, row );
            row = ELM_PLIST( inv, i );
            SET_ELM_PLIST( inv, i, ELM_PLIST( inv, k ) );
            SET_ELM_PLIST( inv, k, row );
        }
        
        /* clear entries                                                   */
        old = ELM_PLIST( list, k );
        end = BLOCKS_GF2VEC(old) + ((len+BIPEB-1)/BIPEB);
        for ( i = 1;  i <= len;  i++ ) {
            if ( i == k )
                continue;
            row = ELM_PLIST( list, i );
            if ( BLOCK_ELM_GF2VEC(row,k) & MASK_POS_GF2VEC(k) ) {

                /* clear <mat>                                             */
                ptQ = &(BLOCK_ELM_GF2VEC(row,k));
                ptP = &(BLOCK_ELM_GF2VEC(old,k));
                while ( ptP < end ) {
                    *ptQ++ ^= *ptP++;
                }

                /* modify <inv>                                            */
                row  = ELM_PLIST( inv, i );
                ptQ  = BLOCKS_GF2VEC(row);
                row  = ELM_PLIST( inv, k );
                ptP  = BLOCKS_GF2VEC(row);
                end2 = ptP + ((len+BIPEB-1)/BIPEB);
                while ( ptP < end2 ) {
                    *ptQ++ ^= *ptP++;
                }
            }
        }
	TakeInterrupt();
    }
    return inv;
}
    


/****************************************************************************
**
*F  InverseGF2Mat( <mat> )  . . . . . . . . . . . . . . inverse of GF2 matrix
**
**  This should be improved to work with mutable GF2 matrices
**
*/

Obj InverseGF2Mat (
    Obj                 mat,
    UInt                mut)
{
    UInt                len;            /* dimension                       */
    Obj                 inv;            /* result                          */
    Obj                 row;            /* row vector                      */
    Obj                 tmp;            /* temporary                       */
    UInt                i;              /* loop variable                   */
    Obj                 old;            /* row from <mat>                  */
    UInt *              ptQ;            /* data block of <row>             */
    UInt *              ptP;            /* data block of source row        */
    UInt *              end;            /* end marker                      */
    Obj                 rtype;

    /* make a structural copy of <mat> as list of GF2 vectors              */
    len = LEN_GF2MAT(mat);

    /* special routes for very small matrices */
    if ( len == 0 ) {
        return CopyObj(mat,1);
    }
    if (len == 1 ) {
      row = ELM_GF2MAT(mat,1);
      if (BLOCKS_GF2VEC(row)[0] & 1)
	{
	  return CopyObj(mat, 1);
	}
      else
	return Fail;
    }
    
    tmp = NEW_PLIST( T_PLIST, len );
    for ( i = len;  0 < i;  i-- ) {
        old = ELM_GF2MAT( mat, i );
        NEW_GF2VEC( row, TYPE_LIST_GF2VEC_IMM, len );
        ptQ = BLOCKS_GF2VEC(old);
        ptP = BLOCKS_GF2VEC(row);
        end = ptP + ((len+BIPEB-1)/BIPEB);
        while ( ptP < end )
            *ptP++ = *ptQ++;
        SET_ELM_PLIST( tmp, i, row );
        CHANGED_BAG(tmp);
    }
    SET_LEN_PLIST(tmp,len);
    inv = InversePlistGF2VecsDesstructive( tmp );
    if (inv == Fail)
      return inv;
    
    /* convert list <inv> into a matrix                                    */
    ResizeBag( inv, SIZE_PLEN_GF2MAT(len) );
    if (mut == 2 ||
	(mut == 1 && IS_MUTABLE_OBJ(mat) && IS_MUTABLE_OBJ(ELM_GF2MAT(mat, 1))))
      rtype = TYPE_LIST_GF2VEC_LOCKED;
    else
      rtype = TYPE_LIST_GF2VEC_IMM_LOCKED;
    for ( i = len;  0 < i;  i-- ) {
      row = ELM_PLIST(inv,i);
      SET_TYPE_POSOBJ(row, rtype);
      SET_ELM_GF2MAT( inv, i, row );
    }
    SET_LEN_GF2MAT( inv, len );
    RetypeBag( inv, T_POSOBJ );
    SET_TYPE_POSOBJ(inv, (mut == 2 || (mut == 1 && IS_MUTABLE_OBJ(mat)))
                             ? TYPE_LIST_GF2MAT
                             : TYPE_LIST_GF2MAT_IMM);
    return inv;
}

/****************************************************************************
**
*F  ShallowCopyVecGF2( <vec> )
**
*/

Obj ShallowCopyVecGF2( Obj vec )
{
  Obj copy;
  UInt len;
  UInt *ptrS;
  UInt *ptrD;
  len = LEN_GF2VEC(vec);
  NEW_GF2VEC( copy, TYPE_LIST_GF2VEC, len);
  ptrS = BLOCKS_GF2VEC(vec);
  ptrD = BLOCKS_GF2VEC(copy);
  memcpy(ptrD, ptrS, NUMBER_BLOCKS_GF2VEC(vec)*sizeof(UInt));
  return copy;
}

/****************************************************************************
**
*F  SemiEchelonPlistGF2Vecs( <mat>, <transformations-needed> )
**
**  The matrix needs to have mutable rows, so it can't be a GF2 mat
**
**  This has changed. There should now be a method for mutable GF2mats as well.
**
**  This function DOES NOT CHECK that the rows are all GF2 vectors
**
**  Does not copy the matrix, may destroy it, may include some
**  of the rows among the returned vectors
*/



static UInt RNheads, RNvectors, RNcoeffs, RNrelns;


Obj SemiEchelonListGF2Vecs( Obj mat, UInt TransformationsNeeded )
{
  UInt nrows, ncols;
  UInt i,j,h;
  Obj heads,vectors, coeffs = 0, relns = 0;
  UInt nvecs, nrels = 0;
  Obj coeffrow = 0;
  Obj row;
  UInt *rowp, *coeffrowp = 0;
  Obj res;
  nrows = LEN_PLIST(mat);
  ncols = LEN_GF2VEC(ELM_PLIST(mat,1));
  heads = NEW_PLIST(T_PLIST_CYC, ncols);
  SET_LEN_PLIST(heads, ncols);
  vectors = NEW_PLIST(T_PLIST_TAB_RECT, nrows);
  nvecs = 0;
  if (TransformationsNeeded)
    {
      coeffs = NEW_PLIST(T_PLIST_TAB_RECT, nrows);
      relns  = NEW_PLIST(T_PLIST_TAB_RECT, nrows);
      nrels = 0;
    }
  for (i = 1; i <= ncols; i++)
    SET_ELM_PLIST(heads, i, INTOBJ_INT(0));
  for (i = 1; i <= nrows; i++)
    {
      row = ELM_PLIST(mat, i);
      if (TransformationsNeeded)
	{
	  NEW_GF2VEC(coeffrow, TYPE_LIST_GF2VEC, nrows);
	  BLOCK_ELM_GF2VEC( coeffrow, i) |= MASK_POS_GF2VEC(i);
	}
      
      /* No garbage collection risk from here */
      rowp = BLOCKS_GF2VEC(row);
      if (TransformationsNeeded)
	coeffrowp = BLOCKS_GF2VEC(coeffrow);
      for (j = 1; j <= ncols; j++)
	{
	  h = INT_INTOBJ(ELM_PLIST(heads, j));
	  if (h != 0)
	    {
	      if (rowp[(j-1)/BIPEB] & MASK_POS_GF2VEC(j))
		{
		  AddGF2VecToGF2Vec(rowp, BLOCKS_GF2VEC(ELM_PLIST(vectors,h)),ncols);
		  if (TransformationsNeeded)
		    AddGF2VecToGF2Vec(coeffrowp, BLOCKS_GF2VEC(ELM_PLIST(coeffs,h)),nrows);
		}
	    }
	}
      j = 1;
      while (j <= ncols && !*rowp)
	{
	  j += BIPEB;
	  rowp++;
	}
      while ( j <= ncols && !(*rowp & MASK_POS_GF2VEC(j)))
	j++;

      /* garbage collection OK again after here */
      if (j <= ncols)
	{
	  SET_ELM_PLIST(vectors, ++nvecs, row);
          CHANGED_BAG(vectors);    /* Could be an old bag by now. Max. */
	  SET_LEN_PLIST(vectors, nvecs);
	  SET_ELM_PLIST( heads, j, INTOBJ_INT(nvecs));
	  if (TransformationsNeeded)
	    {
	      SET_ELM_PLIST(coeffs, nvecs, coeffrow);
              CHANGED_BAG(coeffs);    /* Could be an old bag by now. Max. */
	      SET_LEN_PLIST(coeffs, nvecs);
	    }
	}
      else if (TransformationsNeeded)
	{
	  SET_ELM_PLIST(relns, ++nrels, coeffrow);
          CHANGED_BAG(relns);    /* Could be an old bag by now. Max. */
	  SET_LEN_PLIST(relns, nrels);
	}
      TakeInterrupt();
    }
  if (RNheads == 0)
    {
      RNheads = RNamName("heads");
      RNvectors = RNamName("vectors");
    }
  res = NEW_PREC( TransformationsNeeded ? 4 : 2);
  AssPRec(res,RNheads,heads);
  AssPRec(res,RNvectors,vectors);
  if (LEN_PLIST(vectors) == 0)
    RetypeBag(vectors, T_PLIST_EMPTY);
  if (TransformationsNeeded)
    {
      if (RNcoeffs == 0)
	{
	  RNcoeffs = RNamName("coeffs");
	  RNrelns = RNamName("relations");
	}
      AssPRec(res,RNcoeffs,coeffs);
      if (LEN_PLIST(coeffs) == 0)
	RetypeBag(coeffs, T_PLIST_EMPTY);
      AssPRec(res,RNrelns,relns);
      if (LEN_PLIST(relns) == 0)
	RetypeBag(relns, T_PLIST_EMPTY);
    }
  SortPRecRNam(res,0);
  return res;
}

/****************************************************************************
**
*F  UInt TriangulizeListGF2Vecs( <mat>, <clearup> ) -- returns the rank
**
**  Again should add a method to work with mutable GF2 matrices
**
*/

UInt TriangulizeListGF2Vecs( Obj mat, UInt clearup)
{
  UInt nrows;
  UInt ncols;
  UInt workcol;
  UInt workrow;
  UInt rank;
  Obj row, row2;
  UInt *rowp, *row2p;
  UInt block;
  UInt mask;
  UInt j;
  nrows = LEN_PLIST( mat );
  ncols = LEN_GF2VEC( ELM_PLIST(mat, 1));
  rank = 0;

  /* Nothing here can cause a garbage collection */
  
  for (workcol = 1; workcol <= ncols; workcol++)
    {
      block = (workcol-1)/BIPEB;
      mask = MASK_POS_GF2VEC(workcol);
      for (workrow = rank+1; workrow <= nrows &&
	     !(BLOCKS_GF2VEC(ELM_PLIST(mat, workrow))[block] & mask); workrow ++)
	;
      if (workrow <= nrows)
	{
	  rank++;
	  row = ELM_PLIST(mat, workrow);
	  if (workrow != rank)
	    {
	      SET_ELM_PLIST(mat, workrow, ELM_PLIST(mat, rank));
	      SET_ELM_PLIST(mat, rank, row);
	    }
	  rowp = BLOCKS_GF2VEC(row);
	  if (clearup)
	    for (j = 1; j < rank; j ++)
	      {
		row2 = ELM_PLIST(mat, j);
		row2p = BLOCKS_GF2VEC(row2);
		if (row2p[block] & mask)
		  AddGF2VecToGF2Vec(row2p,rowp, ncols);
	      }
	  for (j = workrow+1; j <= nrows; j++)
	    {
	      row2 = ELM_PLIST(mat, j);
	      row2p = BLOCKS_GF2VEC(row2);
	      if (row2p[block] & mask)
		AddGF2VecToGF2Vec(row2p,rowp, ncols);
	    }
	  
	}
      TakeInterrupt();
      
    }
  return rank;
}

/****************************************************************************
**
*F * * * * * * * * * * * *  conversion functions  * * * * * * * * * * * * * *
*/


/****************************************************************************
**
*F  PlainGF2Vec( <list> ) . . .  . convert a GF2 vector into an ordinary list
**
**  'PlainGF2Vec' converts the GF2 vector <list> to a plain list.
*/
static Obj IsLockedRepresentationVector;


void PlainGF2Vec (
    Obj                 list )
{
    Int                 len;            /* length of <list>                */
    UInt                i;              /* loop variable                   */
    Obj                 first = 0;          /* first entry                     */
    UInt                tnum;


    /* check for representation lock */
    if (True == DoFilter( IsLockedRepresentationVector, list))
      ErrorMayQuit("Cannot convert a locked GF2 vector into a plain list", 0, 0);
    
    /* resize the list and retype it, in this order                        */
    len = LEN_GF2VEC(list);

    if (len == 0)
      tnum = T_PLIST_EMPTY;
    else
      tnum = T_PLIST_FFE;
    if (!IS_MUTABLE_OBJ(list))
      tnum += IMMUTABLE;
    RetypeBag( list, tnum);
    GROW_PLIST( list, (UInt)len );
    SET_LEN_PLIST( list, len );

    /* keep the first entry because setting the second destroys the first  */
    if (len == 0)
      SET_ELM_PLIST( list, 1 , 0);
    else
      first = ELM_GF2VEC(list,1);

    /* wipe out the first entry of the GF2 vector (which becomes the  second */
    /* entry of the plain list, in case the list has length 1.             */
    if (len == 1)
      SET_ELM_PLIST( list, 2, 0 );
    
    /* replace the bits by 'GF2One' or 'GF2Zero' as the case may be        */
    /* this must of course be done from the end of the list backwards      */
    for ( i = len;  1 < i;  i-- )
        SET_ELM_PLIST( list, i, ELM_GF2VEC( list, i ) );
    if (len != 0)
	SET_ELM_PLIST( list, 1, first );

    CHANGED_BAG(list);
}


/****************************************************************************
**
*F  PlainGF2Mat( <list> ) . . .  . convert a GF2 matrix into an ordinary list
**
**  'PlainGF2Mat' converts the GF2 matrix <list> to a plain list.
*/
void PlainGF2Mat (
    Obj                 list )
{
    Int                 len;            /* length of <list>                */
    UInt                i;              /* loop variable                   */

    /* resize the list and retype it, in this order                        */
    len = LEN_GF2MAT(list);
    RetypeBag( list, IS_MUTABLE_OBJ(list) ? T_PLIST : T_PLIST+IMMUTABLE );
    SET_LEN_PLIST( list, len );

    /* shift the entries to the left                                       */
    for ( i = 1;  i <= len;  i++ ) {
        SET_ELM_PLIST( list, i, ELM_GF2MAT( list, i ) );
    }
    SHRINK_PLIST( list, len );
    CHANGED_BAG(list);
}


/****************************************************************************
**
*F  ConvGF2Vec( <list> )  . . . . . . convert a list into a GF2 vector object
*/
void ConvGF2Vec (
    Obj                 list )
{
    Int                 len;            /* logical length of the vector    */
    Int                 i;              /* loop variable                   */
    UInt                block;          /* one block of the boolean list   */
    UInt                bit;            /* one bit of a block              */
    Obj                 x;
        
    /* already in the correct representation                               */
    if ( IS_GF2VEC_REP(list) ) {
        return;
    }

    /* Otherwise make it a plain list so that we will know where it keeps
       its data -- could do much better in the case of GF(2^n) vectors that actually
       lie over GF(2) */

    if (IS_VEC8BIT_REP(list))
      PlainVec8Bit(list);
    else
      PLAIN_LIST( list );
    
    /* change its representation                                           */
    len   = LEN_PLIST(list);

    /* We may have to resize the bag now because a length 1
       plain list is shorter than a length 1 GF2VEC */
    if (SIZE_PLEN_GF2VEC(len) > SIZE_OBJ(list))
      ResizeBag( list, SIZE_PLEN_GF2VEC(len) );

    /* now do the work */
    block = 0;
    bit   = 1;
    for ( i = 1;  i <= len;  i++ ) {
      x = ELM_PLIST(list, i);
      if (x == GF2One)
	block |= bit;
      else if (x != GF2Zero)
	{
	  /* might be GF(2) elt written over bigger field */
	  if (EQ(x, GF2One))
	    block |= bit;
	  else if (!EQ(x, GF2Zero))
	    ErrorMayQuit("COPY_GF2VEC: argument must be a list of GF2 elements", 0L, 0L);
	}
      
      bit = bit << 1;
      if ( bit == 0 || i == len ) {
	BLOCK_ELM_GF2VEC(list,i) = block;
	block = 0;
	bit   = 1;
      }
    }

    /* retype and resize bag                                               */
    ResizeWordSizedBag( list, SIZE_PLEN_GF2VEC(len) );
    SET_LEN_GF2VEC( list, len );
    if ( IS_PLIST_MUTABLE( list ) ) {
        SetTypeDatObj( list, TYPE_LIST_GF2VEC);
    } else {
        SetTypeDatObj( list, TYPE_LIST_GF2VEC_IMM);
    }
    RetypeBag( list, T_DATOBJ );
}


/****************************************************************************
**
*F  FuncCONV_GF2VEC( <self>, <list> ) . . . . . convert into a GF2 vector rep
*/
Obj FuncCONV_GF2VEC (
    Obj                 self,
    Obj                 list )
{
    /* check whether <list> is a GF2 vector                               */
    ConvGF2Vec(list);

    /* return nothing                                                      */
    return 0;
}


/****************************************************************************
**
*F  NewGF2Vec( <list> )  . . . . . . convert a list into a GF2 vector object
** 
**  This is a non-destructive counterpart of ConvGF2Vec
*/
Obj NewGF2Vec (
    Obj                 list )
{
    Int                 len;            /* logical length of the vector    */
    Int                 i;              /* loop variable                   */
    UInt                block;          /* one block of the boolean list   */
    UInt                bit;            /* one bit of a block              */
    Obj                 x;
    Obj                 res;            /* resulting GF2 vector object     */
    
    /* already in the correct representation                               */
    if ( IS_GF2VEC_REP(list) ) {
        res = ShallowCopyVecGF2(list);
        if (!IS_MUTABLE_OBJ(list))
          SetTypeDatObj(res, TYPE_LIST_GF2VEC_IMM);
        return res;
    }

    if (!IS_LIST(list)) {
        ErrorMayQuit("COPY_GF2VEC: argument must be a list of GF2 elements", 0L, 0L);
    }
    if (!IS_PLIST(list)) {
        list = SHALLOW_COPY_OBJ(list);
        // TODO: if list is in 8bit rep, we could do better
        if (IS_VEC8BIT_REP(list))
            PlainVec8Bit(list);
        else
            PLAIN_LIST( list );
    }
    
    len = LEN_PLIST(list);
    NEW_GF2VEC( res, TYPE_LIST_GF2VEC, len );
    
    /* now do the work */
    block = 0;
    bit   = 1;
    for ( i = 1;  i <= len;  i++ ) {
      x = ELM_PLIST(list, i);
      if (x == GF2One)
	    block |= bit;
      else if (x != GF2Zero)
	    {
	      /* might be GF(2) elt written over bigger field */
	      if (EQ(x, GF2One))
	        block |= bit;
	      else if (!EQ(x, GF2Zero))
            ErrorMayQuit("COPY_GF2VEC: argument must be a list of GF2 elements", 0L, 0L);
	    }
      
      bit = bit << 1;
      if ( bit == 0 || i == len ) {
	    BLOCK_ELM_GF2VEC(res,i) = block; /* only changed list to res */
	    block = 0;
	    bit   = 1;
      }
    }

    /* mutability should be inherited from the argument */
    if ( IS_PLIST_MUTABLE( list ) )
        SetTypeDatObj( res , TYPE_LIST_GF2VEC);
    else
        SetTypeDatObj( res , TYPE_LIST_GF2VEC_IMM);
    
    return res;
}


/****************************************************************************
**
*F  FuncCOPY_GF2VEC( <self>, <list> ) . . . . . convert into a GF2 vector rep
**
**  This is a non-destructive counterpart of FuncCONV_GF2VEC
*/
Obj FuncCOPY_GF2VEC (
    Obj                 self,
    Obj                 list )
{
    /* check whether <list> is a GF2 vector                               */
    list = NewGF2Vec(list);

    return list;
}

/****************************************************************************
**
*F FuncCONV_GF2MAT (<self>, <list> ) . . . convert into a GF2 matrix rep
**
** <list> should be a a list of compressed GF2 vectors
**  
*/
Obj FuncCONV_GF2MAT( Obj self, Obj list)
{
  UInt len, i;
  Obj tmp;
  UInt mut;
  len = LEN_LIST(list);
  if (len == 0)
    return (Obj)0;
  
  PLAIN_LIST(list);
  GROW_PLIST(list, len+1);
  for (i = len; i > 0 ; i--)
    {
      tmp = ELM_PLIST(list, i);
      if (!IS_GF2VEC_REP(tmp))
	{
	  int j;
	  for (j = i+1; j <= len; j++)
	    {
	      tmp = ELM_PLIST(list, j+1);
	      SET_ELM_PLIST(list,j,tmp);
	    }
	  ErrorMayQuit("CONV_GF2MAT: argument must be a list of compressed GF2 vectors", 0L, 0L);
	}
      SetTypeDatObj(tmp, IS_MUTABLE_OBJ(tmp) ? TYPE_LIST_GF2VEC_LOCKED: TYPE_LIST_GF2VEC_IMM_LOCKED);
      SET_ELM_PLIST(list, i+1, tmp);
    }
  SET_ELM_PLIST(list,1,INTOBJ_INT(len));
  mut = IS_PLIST_MUTABLE(list);
  RetypeBag(list, T_POSOBJ);
  SET_TYPE_POSOBJ(list, mut ? TYPE_LIST_GF2MAT : TYPE_LIST_GF2MAT_IMM);
  return (Obj) 0;
}


/****************************************************************************
**
*F  FuncPLAIN_GF2VEC( <self>, <list> ) . . .  convert back into ordinary list
*/
Obj FuncPLAIN_GF2VEC (
    Obj                 self,
    Obj                 list )
{
    /* check whether <list> is a GF2 vector                                */
    while ( ! IS_GF2VEC_REP(list) ) {
        list = ErrorReturnObj(
            "PLAIN_GF2VEC: <list> must be a GF2 vector (not a %s)",
            (Int)TNAM_OBJ(list), 0L,
            "you can replace <list> via 'return <list>;'" );
    }
    PlainGF2Vec(list);

    /* return nothing                                                      */
    return 0;
}




/****************************************************************************
**
*F  revertbits -- utility function to reverse bit orders
*/


/*   A list of flip values for bytes (i.e. ..xyz -> zyx..) */

static const UInt1 revertlist [] ={
 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, 8, 
  136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, 4, 
  132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, 12, 
  140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, 2, 
  130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, 10, 
  138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, 6, 
  134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, 14, 
  142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, 1, 
  129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 9, 
  137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5, 
  133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13, 
  141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 3, 
  131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, 11, 
  139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, 7, 
  135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, 15, 
  143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255
};

/* Takes an UInt a on n bits and returns the Uint obtained by reverting the
 * bits */
UInt revertbits(UInt a, Int n)
{
  UInt b,c;
  b=0;
  while (n>8) {
    c=a&0xff; /* last byte */
    a = a>>8;
    b = b<<8;
    b += (UInt) revertlist[(UInt1)c]; /* add flipped */
    n -=8;
  }
  /* cope with the last n bits */
  a &= 0xff;
  b= b<<n;
  c=(UInt) revertlist[(UInt1)a];
  c = c>> (8-n);
  b+=c;
  return b;
}

/****************************************************************************
**
*F  Cmp_GF2Vecs( <vl>, <vr> )   compare GF2 vectors -- internal
**                                    returns -1, 0 or 1
*/
Int Cmp_GF2VEC_GF2VEC (
    Obj                 vl,
    Obj                 vr )
{
    UInt                i;              /* loop variable                   */
    UInt *              bl;             /* block of <vl>                   */
    UInt *              br;             /* block of <vr>                   */
    UInt                len,lenl,lenr;  /* length of the list              */
    UInt a,b,nb;

    /* get and check the length                                            */
    lenl = LEN_GF2VEC(vl);
    lenr = LEN_GF2VEC(vr);
    nb=NUMBER_BLOCKS_GF2VEC(vl);
    a=NUMBER_BLOCKS_GF2VEC(vr);
    if (a<nb) {
      nb = a;
    }

    /* check all blocks                                                    */
    bl = BLOCKS_GF2VEC(vl);
    br = BLOCKS_GF2VEC(vr);
    for ( i = nb;  1 < i;  i--, bl++, br++ ) {
	/* comparison is numeric of the reverted lists*/
      if (*bl != *br)
	{
	  a=revertbits(*bl,BIPEB);
	  b=revertbits(*br,BIPEB);
	  if ( a < b )
            return -1;
	  else return 1;
	}
    }
    

    /* The last block remains */
    len=lenl;
    if (len>lenr) {
      len=lenr;
    }

    /* are both vectors length 0? */
    if (len == 0 ) return 0;

    /* is there still a full block in common? */
    len = len % BIPEB;
    if (len == 0) {
      a=revertbits(*bl,BIPEB);
      b=revertbits(*br,BIPEB);
    }
    else {
      a=revertbits(*bl,len);
      b=revertbits(*br,len);
    }

    if (a<b)
      return -1;
    if (a>b)
      return 1;
    
    /* blocks still the same --left length must be smaller to be true */
    if (lenr>lenl)
      return -1;
    if (lenl > lenr)
      return 1;
    
    return 0;

}


/****************************************************************************
**
*F  FuncEQ_GF2VEC_GF2VEC( <self>, <vl>, <vr> )   test equality of GF2 vectors
*/
Obj FuncEQ_GF2VEC_GF2VEC (
    Obj                 self,
    Obj                 vl,
    Obj                 vr )
{
  /* we can do this case MUCH faster if we just want equality */
  if (LEN_GF2VEC(vl)  != LEN_GF2VEC(vr))
    return False;
  return (Cmp_GF2VEC_GF2VEC(vl,vr) == 0) ? True : False;
}


/****************************************************************************
**
*F  FuncLEN_GF2VEC( <self>, <list> )  . . . . . . . .  length of a GF2 vector
*/
Obj FuncLEN_GF2VEC (
    Obj                 self,
    Obj                 list )
{
    return INTOBJ_INT(LEN_GF2VEC(list));
}


/****************************************************************************
**
*F  FuncELM0_GF2VEC( <self>, <list>, <pos> )  . select an elm of a GF2 vector
**
**  'ELM0_GF2VEC'  returns the element at the  position  <pos> of the boolean
**  list <list>, or `Fail' if <list> has no assigned  object at <pos>.  It is
**  the  responsibility of  the caller to   ensure  that <pos> is  a positive
**  integer.
*/
Obj FuncELM0_GF2VEC (
    Obj                 self,
    Obj                 list,
    Obj                 pos )
{
    UInt                p;

    
    if (!IS_INTOBJ(pos))
      ErrorMayQuit("ELM0_GF2VEC: position must be a small integer, not a %s",
		(Int)TNAM_OBJ(pos),0L);
    p = INT_INTOBJ(pos);
    if ( LEN_GF2VEC(list) < p ) {
        return Fail;
    }
    else {
        return ELM_GF2VEC( list, p );
    }
}


/****************************************************************************
**
*F  FuncELM_GF2VEC( <self>, <list>, <pos> ) . . select an elm of a GF2 vector
**
**  'ELM_GF2VEC' returns the element at the position <pos>  of the GF2 vector
**  <list>.   An  error  is signalled  if  <pos>  is  not bound.    It is the
**  responsibility of the caller to ensure that <pos> is a positive integer.
*/
Obj FuncELM_GF2VEC (
    Obj                 self,
    Obj                 list,
    Obj                 pos )
{
    UInt                p;

    if (!IS_INTOBJ(pos))
      ErrorMayQuit("ELM_GF2VEC: position must be a small integer, not a %s",
		(Int)TNAM_OBJ(pos),0L);
    p = INT_INTOBJ(pos);
    if ( LEN_GF2VEC(list) < p ) {
        ErrorReturnVoid(
            "List Element: <list>[%d] must have an assigned value",
            p, 0L, "you can 'return;' after assigning a value" );
        return ELM_LIST( list, p );
    }
    else {
        return ELM_GF2VEC( list, p );
    }
}


/****************************************************************************
**
*F  FuncELMS_GF2VEC( <self>, <list>, <poss> ) . . . sublist from a GF2 vector
**
**  'ELMS_GF2VEC' returns a new list containing  the elements at the position
**  given in    the   list  <poss> from  the   vector   <list>.   It  is  the
**  responsibility of the caller to ensure that <poss>  is dense and contains
**  only positive integers.  An error is signalled if an element of <poss> is
**  larger than the length of <list>.
*/
Obj FuncELMS_GF2VEC (
    Obj                 self,
    Obj                 list,
    Obj                 poss )
{
    Obj                 elms;           /* selected sublist, result        */
    Int                 lenList;        /* length of <list>                */
    Int                 lenPoss;        /* length of positions             */
    Int                 pos;            /* position as integer             */
    Int                 inc;            /* increment in a range            */
    Int                 i;              /* loop variable                   */
    Obj                 apos;

    /* get the length of <list>                                            */
    lenList = LEN_GF2VEC(list);

    /* general code for arbritrary lists, which are ranges                 */
    if ( ! IS_RANGE(poss) ) {

        /* get the length of <positions>                                   */
        lenPoss = LEN_LIST(poss);

        /* make the result vector                                          */
        NEW_GF2VEC( elms, TYPE_LIST_GF2VEC, lenPoss );

        /* loop over the entries of <positions> and select                 */
        for ( i = 1;  i <= lenPoss;  i++ ) {

            /* get next position                                           */

	  apos = ELM0_LIST( poss, i);
	  if (!apos || !IS_INTOBJ(apos))
	    ErrorMayQuit("ELMS_GF2VEC: error at position %d in positions list, entry must be bound to a small integer",
		      i, 0L);
	  pos = INT_INTOBJ( apos );
	  if ( lenList < pos ) {
	    ErrorMayQuit( "List Elements: <list>[%d] must have a value",
		       pos, 0L );
	    return 0;
	  }
	  
	  /* assign the element into <elms>                              */
	  if ( ELM_GF2VEC( list, pos ) == GF2One ) {
	    BLOCK_ELM_GF2VEC(elms,i) |= MASK_POS_GF2VEC(i);
	  }
        }
	
    }

    /* special code for ranges                                             */
    else {

        /* get the length of <positions>, the first elements, and the inc. */
        lenPoss = GET_LEN_RANGE(poss);
        pos = GET_LOW_RANGE(poss);
        inc = GET_INC_RANGE(poss);

        /* check that no <position> is larger than <lenList>               */
        if ( lenList < pos ) {
            ErrorMayQuit( "List Elements: <list>[%d] must have a value",
                       pos, 0L );
            return 0;
        }
        if ( lenList < pos + (lenPoss-1) * inc ) {
            ErrorMayQuit( "List Elements: <list>[%d] must have a value",
                       pos + (lenPoss-1) * inc, 0L );
            return 0;
        }

        /* make the result vector                                          */
        NEW_GF2VEC( elms, TYPE_LIST_GF2VEC, lenPoss );
	
	/* increment 1 ranges is a block copy */
	if (inc == 1)
	  CopySection_GF2Vecs(list, elms, pos, 1, lenPoss);

        /* loop over the entries of <positions> and select                 */
        else {
           for ( i = 1;  i <= lenPoss;  i++, pos += inc ) {
                if ( ELM_GF2VEC(list,pos) == GF2One ) {
                    BLOCK_ELM_GF2VEC(elms,i) |= MASK_POS_GF2VEC(i);
                }
            }
        }
    }

    /* return the result                                                   */
    return elms;
}


/****************************************************************************
**
*F  FuncASS_GF2VEC( <self>, <list>, <pos>, <elm> ) set an elm of a GF2 vector
**
**  'ASS_GF2VEC' assigns the element  <elm> at the position  <pos> to the GF2
**  vector <list>.
**
**  It is the responsibility of the caller  to ensure that <pos> is positive,
**  and that <elm> is not 0.
*/

Obj FuncASS_GF2VEC (
    Obj                 self,
    Obj                 list,
    Obj                 pos,
    Obj                 elm )
{
    UInt                p;

    /* check that <list> is mutable                                        */
    if ( ! IS_MUTABLE_OBJ(list) ) {
        ErrorReturnVoid(
            "List Assignment: <list> must be a mutable list",
            0L, 0L,
            "you can 'return;' and ignore the assignment" );
        return 0;
    }

    /* get the position                                                    */
    if (!IS_INTOBJ(pos))
      ErrorMayQuit("ASS_VEC8BIT: position must be a small integer, not a %s",
		(Int)TNAM_OBJ(pos),0L);
    p = INT_INTOBJ(pos);

    /* if <elm> is Z(2) or 0*Z(2) and the position is OK, keep rep         */
    if ( p <= LEN_GF2VEC(list)+1 ) {
        if ( LEN_GF2VEC(list)+1 == p ) {
	  if (DoFilter(IsLockedRepresentationVector, list) == True)
	    ErrorMayQuit("Assignment forbidden beyond the end of locked GF2 vector", 0, 0);
	  ResizeWordSizedBag( list, SIZE_PLEN_GF2VEC(p) );
	  SET_LEN_GF2VEC( list, p );
        }
        if ( EQ(GF2One,elm) ) {
            BLOCK_ELM_GF2VEC(list,p) |= MASK_POS_GF2VEC(p);
        }
        else if ( EQ(GF2Zero,elm) ) {
            BLOCK_ELM_GF2VEC(list,p) &= ~MASK_POS_GF2VEC(p);
        }
	else if (IS_FFE(elm) && CHAR_FF(FLD_FFE(elm)) == 2 && DEGR_FF(FLD_FFE(elm)) <= 8)
	  {
	    /*	    Pr("Rewriting GF2 vector over larger field",0,0); */
	    RewriteGF2Vec(list, SIZE_FF(FLD_FFE(elm)));
	    FuncASS_VEC8BIT(self, list, pos, elm);
	  }
        else
	  {
	    /* 	    Pr("arbitrary assignment (GF2)",0,0); */
	    PlainGF2Vec(list);
	    ASS_LIST( list, p, elm );
	  }
    }
    else {
      /*       Pr("arbitrary assignment 2 (GF2)",0,0); */
        PlainGF2Vec(list);
        ASS_LIST( list, p, elm );
    }
    return 0;
}

/****************************************************************************
**
*F  FuncPLAIN_GF2MAT( <self>, <list> ) . . .  convert back into ordinary list
*/
Obj FuncPLAIN_GF2MAT (
    Obj                 self,
    Obj                 list )
{
    PlainGF2Mat(list);

    /* return nothing                                                      */
    return 0;
}


/****************************************************************************
**
*F  FuncASS_GF2MAT( <self>, <list>, <pos>, <elm> ) set an elm of a GF2 matrix
**
**  'ASS_GF2MAT' assigns the element  <elm> at the position  <pos> to the GF2
**  matrix <list>.
**
**  It is the responsibility of the caller  to ensure that <pos> is positive,
**  and that <elm> is not 0.
*/
Obj FuncASS_GF2MAT (
    Obj                 self,
    Obj                 list,
    Obj                 pos,
    Obj                 elm )
{
    UInt                p;

    /* check that <list> is mutable                                        */
    if ( ! IS_MUTABLE_OBJ(list) ) {
        ErrorReturnVoid(
            "List Assignment: <list> must be a mutable list",
            0L, 0L,
            "you can 'return;' and ignore the assignment" );
        return 0;
    }

    /* get the position                                                    */
    if (!IS_INTOBJ(pos))
      ErrorMayQuit("ASS_GF2MAT: position must be a small integer, not a %s",
		(Int)TNAM_OBJ(pos),0L);
    p = INT_INTOBJ(pos);

    /* if <elm> is a GF2 vector and the length is OK, keep the rep         */
    if ( ! IS_GF2VEC_REP(elm)  ) {
        PlainGF2Mat(list);
        ASS_LIST( list, p, elm );
    }
    else if ( p == 1 && 1 >= LEN_GF2MAT(list) ) {
        ResizeBag( list, SIZE_PLEN_GF2MAT(p) );
	SetTypeDatObj(elm, IS_MUTABLE_OBJ(elm) ? TYPE_LIST_GF2VEC_LOCKED : TYPE_LIST_GF2VEC_IMM_LOCKED);
        SET_ELM_GF2MAT( list, p, elm );
	CHANGED_BAG(list);
    }
    else if ( p > LEN_GF2MAT(list)+1 ) {
        PlainGF2Mat(list);
        ASS_LIST( list, p, elm );
    }
    else if ( LEN_GF2VEC(elm) == LEN_GF2VEC(ELM_GF2MAT(list,1)) ) {
        if ( LEN_GF2MAT(list)+1 == p ) {
            ResizeBag( list, SIZE_PLEN_GF2MAT(p) );
            SET_LEN_GF2MAT( list, p );
        }
	SetTypeDatObj(elm, IS_MUTABLE_OBJ(elm) ? TYPE_LIST_GF2VEC_LOCKED : TYPE_LIST_GF2VEC_IMM_LOCKED);
        SET_ELM_GF2MAT( list, p, elm );
        CHANGED_BAG(list);
    }
    else {
        PlainGF2Mat(list);
        ASS_LIST( list, p, elm );
    }
    return 0;
}


/****************************************************************************
**
*F  FuncELM_GF2MAT( <self>, <mat>, <row> ) . . . select a row of a GF2 matrix
**
*/
Obj FuncELM_GF2MAT( Obj self, Obj mat, Obj row )
{
    if (!IS_POS_INTOBJ(row)) {
        ErrorMayQuit("ELM_GF2MAT: position must be a small integer, not a %s",
                     (Int)TNAM_OBJ(row), 0L);
    }
    UInt r = INT_INTOBJ(row);
    if (LEN_GF2MAT(mat) < r) {
        ErrorMayQuit("row index %d exceeds %d, the number of rows", r, LEN_GF2MAT(mat));
    }
    return ELM_GF2MAT(mat, r);
}


/****************************************************************************
**
*F  FuncUNB_GF2VEC( <self>, <list>, <pos> ) . unbind position of a GF2 vector
**
**  'UNB_GF2VEC' unbind  the element at  the position  <pos> in  a GF2 vector
**  <list>.
**
**  It is the responsibility of the caller  to ensure that <pos> is positive.
*/
Obj FuncUNB_GF2VEC (
    Obj                 self,
    Obj                 list,
    Obj                 pos )
{
    UInt                p;

    /* check that <list> is mutable                                        */
    if ( ! IS_MUTABLE_OBJ(list) ) {
        ErrorReturnVoid(
            "List Unbind: <list> must be a mutable list",
            0L, 0L,
            "you can 'return;' and ignore the unbind" );
        return 0;
    }

    if (DoFilter(IsLockedRepresentationVector, list) == True)
    {
      ErrorReturnVoid( "Unbind forbidden on locked GF2 vector",
		          0L, 0L,
            "you can 'return;' and ignore the operation" );
        return 0;
    }

    /* get the position                                                    */
    if (!IS_INTOBJ(pos))
      ErrorMayQuit("UNB_GF2VEC: position must be a small integer, not a %s",
		(Int)TNAM_OBJ(pos),0L);
    p = INT_INTOBJ(pos);

    /* if we unbind the last position keep the representation              */
    if ( LEN_GF2VEC(list) < p ) {
        ;
    }
    else if ( LEN_GF2VEC(list) == p ) {
        ResizeWordSizedBag( list, SIZE_PLEN_GF2VEC(p-1) );
        SET_LEN_GF2VEC( list, p-1 );
    }
    else {
        PlainGF2Vec(list);
        UNB_LIST( list, p );
    }
    return 0;
}


/****************************************************************************
**
*F  FuncUNB_GF2MAT( <self>, <list>, <pos> ) . unbind position of a GF2 matrix
**
**  'UNB_GF2VEC' unbind  the element at  the position  <pos> in  a GF2 matrix
**  <list>.
**
**  It is the responsibility of the caller  to ensure that <pos> is positive.
*/
Obj FuncUNB_GF2MAT (
    Obj                 self,
    Obj                 list,
    Obj                 pos )
{
    UInt                p;

    /* check that <list> is mutable                                        */
    if ( ! IS_MUTABLE_OBJ(list) ) {
        ErrorReturnVoid(
            "List Unbind: <list> must be a mutable list",
            0L, 0L,
            "you can 'return;' and ignore the unbind" );
        return 0;
    }

    /* get the position                                                    */
    if (!IS_INTOBJ(pos))
      ErrorMayQuit("UNB_GF2MAT: position must be a small integer, not a %s",
		(Int)TNAM_OBJ(pos),0L);
    p = INT_INTOBJ(pos);

    /* if we unbind the last position keep the representation              */
    if ( p > 1 && LEN_GF2MAT(list) < p ) {
        ;
    }
    else if ( LEN_GF2MAT(list) == p ) {
        ResizeBag( list, SIZE_PLEN_GF2MAT(p-1) );
        SET_LEN_GF2MAT( list, p-1 );
    }
    else {
        PlainGF2Mat(list);
        UNB_LIST( list, p );
    }
    return 0;
}


/****************************************************************************
**
*F * * * * * * * * * * * * arithmetic operations  * * * * * * * * * * * * * *
*/


/****************************************************************************
**
*F  FuncZERO_GF2VEC( <self>, <mat> )  . . . . . . . . . . . . zero GF2 vector
**
**  return the zero vector over GF2 of the same length as <mat>.
*/
Obj FuncZERO_GF2VEC (
    Obj                 self,
    Obj                 mat )
{
    Obj                 zero;
    UInt                len;

    /* create a new GF2 vector                                             */
    len = LEN_GF2VEC(mat);
    NEW_GF2VEC( zero, TYPE_LIST_GF2VEC, len );
    return zero;
}

/****************************************************************************
**
*F  FuncZERO_GF2VEC_2( <self>, <len>) . . . . . . . . . zero GF2 vector
**
**  return the zero vector over GF2 of length <len>
*/
Obj FuncZERO_GF2VEC_2 (
    Obj                 self,
    Obj                 len )
{
    Obj                 zero;

    /* create a new GF2 vector*/
    if (!IS_INTOBJ(len))
      ErrorMayQuit("ZERO_GF2VEC2: length must be a small integer, not a %s",
		(Int)TNAM_OBJ(len),0L);
    
    NEW_GF2VEC( zero, TYPE_LIST_GF2VEC, INT_INTOBJ(len) );
    return zero;
}


/****************************************************************************
**
*F  FuncINV_GF2MAT_MUTABLE( <self>, <mat> ) . .. . . . .  inverse GF2 matrix
**
** This might now be redundant, a library method using
**  INVERSE_PLIST_GF2VECS_DESTRUCTIVE
** might do just as good a job
*/
Obj FuncINV_GF2MAT_MUTABLE (
    Obj                 self,
    Obj                 mat )
{
    UInt                len;

    len = LEN_GF2MAT(mat);
    if ( len != 0 ) {
        if ( len != LEN_GF2VEC(ELM_GF2MAT(mat,1)) ) {
            mat = ErrorReturnObj( "<matrix> must be square", 0, 0,
                                  "you can replace <matrix> via 'return <matrix>;'" );
	    return INV(mat);
        }
    }
    return InverseGF2Mat(mat,2);
}

/****************************************************************************
**
*F  FuncINV_GF2MAT_SAME_MUTABILITY( <self>, <mat> ) . ...  inverse GF2 matrix
**
** This might now be redundant, a library method using
**  INVERSE_PLIST_GF2VECS_DESTRUCTIVE
** might do just as good a job
*/
Obj FuncINV_GF2MAT_SAME_MUTABILITY (
    Obj                 self,
    Obj                 mat )
{
    UInt                len;

    len = LEN_GF2MAT(mat);
    if ( len != 0 ) {
        if ( len != LEN_GF2VEC(ELM_GF2MAT(mat,1)) ) {
            mat = ErrorReturnObj( "<matrix> must be square", 0, 0,
                                  "you can replace <matrix> via 'return <matrix>;'" );
	    return INV_MUT(mat);
        }
    }
    return InverseGF2Mat(mat,1);
}

/****************************************************************************
**
*F  FuncINV_GF2MAT_IMMUTABLE( <self>, <mat> ) . .. . . .  inverse GF2 matrix
**
** This might now be redundant, a library method using
**  INVERSE_PLIST_GF2VECS_DESTRUCTIVE
** might do just as good a job
*/
Obj FuncINV_GF2MAT_IMMUTABLE (
    Obj                 self,
    Obj                 mat )
{
    UInt                len;

    len = LEN_GF2MAT(mat);
    if ( len != 0 ) {
        if ( len != LEN_GF2VEC(ELM_GF2MAT(mat,1)) ) {
	  Obj inv;
	  mat = ErrorReturnObj( "<matrix> must be square", 0, 0,
				"you can replace <matrix> via 'return <matrix>;'" );
	  inv = INV(mat);
	  MakeImmutable(inv);
	  return inv;
        }
    }
    return InverseGF2Mat(mat,0);
}


/****************************************************************************
**
*F  FuncINV_PLIST_GF2VECS_DESTRUCTIVE( <self>, <list> ) . . .invert possible GF2 matrix
*/
Obj FuncINV_PLIST_GF2VECS_DESTRUCTIVE( Obj self, Obj list )
{
  UInt len,i;
  Obj row;
  len = LEN_PLIST(list);
  for (i = 1; i <= len; i++)
    {
      row = ELM_PLIST(list,i);
      if (!IS_GF2VEC_REP(row) || LEN_GF2VEC(row) != len)
	return TRY_NEXT_METHOD;
    }
  if ( len == 0 ) {
    return CopyObj(list,1);
  }
  if (len == 1 ) {
    row = ELM_PLIST(list,1);
    if (BLOCKS_GF2VEC(row)[0] & 1)
      {
	return CopyObj(list, 1);
	}
    else
      return Fail;
  }
  return InversePlistGF2VecsDesstructive(list);
  
}


/****************************************************************************
**
*F  FuncSUM_GF2VEC_GF2VEC( <self>, <vl>, <vr> ) . . . . .  sum of GF2 vectors
**
**  'FuncSUM_GF2VEC_GF2VEC' returns the sum  of the two gf2-vectors <vl>  and
**  <vr>.  The sum is a new gf2-vector, where each element is  the sum of the
**  corresponding entries of <vl>  and <vr>.  The  major work is done  in the
**  routine   'AddPartialGF2VecGF2Vec'     which     is         called   from
**  'FuncSUM_GF2VEC_GF2VEC'.
**
**  'FuncSUM_GF2VEC_GF2VEC'  is  an improved  version of 'SumListList', which
**  does not call 'SUM' but uses bit operations instead.
*/
Obj FuncSUM_GF2VEC_GF2VEC (
    Obj                 self,
    Obj                 vl,
    Obj                 vr )
{
    Obj                 sum;            /* sum, result                     */
    UInt ll,lr;

    ll = LEN_GF2VEC(vl);
    lr = LEN_GF2VEC(vr);
    
    
    if (ll < lr)
      {
	sum = ShallowCopyVecGF2(vr);
	AddGF2VecToGF2Vec(BLOCKS_GF2VEC(sum),BLOCKS_GF2VEC(vl),ll);
      }
    else
      {
	sum = ShallowCopyVecGF2(vl);
	AddGF2VecToGF2Vec(BLOCKS_GF2VEC(sum),BLOCKS_GF2VEC(vr),lr);
      }

    if (!IS_MUTABLE_OBJ(vl) && !IS_MUTABLE_OBJ(vr))
        SET_TYPE_POSOBJ(sum, TYPE_LIST_GF2VEC_IMM);

    return sum;
}

/****************************************************************************
**
*F  FuncMULT_ROW_VECTOR_GF2VECS_2( <self>, <vl>, <mul> )
**                                      . . . . .  sum of GF2 vectors
**
*/
Obj FuncMULT_ROW_VECTOR_GF2VECS_2 (
    Obj                 self,
    Obj                 vl,
    Obj                 mul )
{
    if (EQ(mul,GF2One))
      return (Obj) 0;
    else if (EQ(mul,GF2Zero))
      {
	AddCoeffsGF2VecGF2Vec(vl,vl);
	return (Obj) 0;
      }
    else
      return TRY_NEXT_METHOD;
}


/****************************************************************************
**
*F  FuncPROD_GF2VEC_GF2VEC( <self>, <vl>, <vr> )  . .  product of GF2 vectors
**
**  'FuncPROD_GF2VEC_GF2VEC' returns the product of  the two GF2 vectors <vl>
**  and <vr>.  The product is either `GF2One' or `GF2Zero'.
*/
Obj FuncPROD_GF2VEC_GF2VEC (
    Obj                 self,
    Obj                 vl,
    Obj                 vr )
{
    return ProdGF2VecGF2Vec( vl, vr );
}


/****************************************************************************
**
*F  FuncPROD_GF2VEC_GF2MAT( <self>, <vl>, <vr> ) product of GF2 vector/matrix
**
**  'FuncPROD_GF2VEC_GF2MAT' returns  the product of the GF2 vectors <vl> and
**  the GF2 matrix <vr>.
**
**  The  product is  again a  GF2 vector.  It  is  the  responsibility of the
**  caller to ensure that <vl> is a  GF2 vector, <vr>  a GF2 matrix.
*/
Obj FuncPROD_GF2VEC_GF2MAT (
    Obj                 self,
    Obj                 vl,
    Obj                 vr )
{
    return ProdGF2VecGF2Mat( vl, vr );
}

/****************************************************************************
**
*F  FuncPROD_GF2MAT_GF2MAT( <self>, <ml>, <mr> ) product of GF2 vector/matrix
**
**  'FuncPROD_GF2MAT_GF2MAT' returns  the product of the GF2 matricess <ml> and
**  <mr>.
**
**  The  product is  again a  GF2 matrix.  It  is  the  responsibility of the
**  caller to ensure that <ml> and <mr> are  GF2 matrices
*/
Obj FuncPROD_GF2MAT_GF2MAT (
    Obj                 self,
    Obj                 ml,
    Obj                 mr )
{
  UInt lenl = LEN_GF2MAT(ml);
  UInt lenm;
  if (lenl >= 128)
    {
      lenm = LEN_GF2VEC(ELM_GF2MAT(ml,1));
      if (lenm >= 128 && lenm == LEN_GF2MAT(mr) && LEN_GF2VEC(ELM_GF2MAT(mr,1)) >= 128)
	{
	  return ProdGF2MatGF2MatAdvanced( ml, mr, 8, (lenm+255)/256);
	}
    }
    return ProdGF2MatGF2MatSimple( ml, mr );
}

/****************************************************************************
**
*F  FuncPROD_GF2MAT_GF2MAT_SIMPLE( <self>, <ml>, <mr> ) product of GF2 vector/matrix
**
**  'FuncPROD_GF2MAT_GF2MAT' returns  the product of the GF2 matricess <ml> and
**  <mr>. It never uses grease or blocking.
**
**  The  product is  again a  GF2 matrix.  It  is  the  responsibility of the
**  caller to ensure that <ml> and <mr> are  GF2 matrices
*/
Obj FuncPROD_GF2MAT_GF2MAT_SIMPLE (
    Obj                 self,
    Obj                 ml,
    Obj                 mr )
{
    return ProdGF2MatGF2MatSimple( ml, mr );
}


/****************************************************************************
**
*F  FuncPROD_GF2MAT_GF2MAT_ADVANCED( <self>, <ml>, <mr>, <greaselevel>, <blocksize> ) 
**
**  'FuncPROD_GF2MAT_GF2MAT_ADVANCED' returns  the product of the GF2 matricess <ml> and
**  <mr> using grease level <greaselevel> and block size <blocksize>
**
**  The  product is  again a  GF2 matrix.  It  is  the  responsibility of the
**  caller to ensure that <ml> and <mr> are  GF2 matrices
*/
Obj FuncPROD_GF2MAT_GF2MAT_ADVANCED (
    Obj                 self,
    Obj                 ml,
    Obj                 mr,
    Obj                 greaselevel,
    Obj                 blocksize)
{
    return ProdGF2MatGF2MatAdvanced( ml, mr, INT_INTOBJ(greaselevel), INT_INTOBJ(blocksize) );
}


/****************************************************************************
**
*F  FuncPROD_GF2MAT_GF2VEC( <self>, <vl>, <vr> ) product of GF2 matrix/vector
**
**  'FuncPROD_GF2VEC_GF2MAT' returns  the product of the GF2 matrix  <vl> and
**  the GF2 vector <vr>.
**
**  The  product is  again a  GF2 vector.  It  is  the  responsibility of the
**  caller to ensure that <vr> is a  GF2 vector, <vl>  a GF2 matrix.
*/
Obj FuncPROD_GF2MAT_GF2VEC (
    Obj                 self,
    Obj                 vl,
    Obj                 vr )
{
    return ProdGF2MatGF2Vec( vl, vr );
}


/****************************************************************************
**
*F  FuncADDCOEFFS_GF2VEC_GF2VEC_MULT( <self>, <vl>, <vr>, <mul> ) GF2 vectors
*/
Obj FuncADDCOEFFS_GF2VEC_GF2VEC_MULT (
    Obj                 self,
    Obj                 vl,
    Obj                 vr,
    Obj                 mul )
{
    /* do nothing if <mul> is zero                                         */
    if ( EQ(mul,GF2Zero) ) {
        return INTOBJ_INT( RightMostOneGF2Vec(vl) );
    }

    /* add if <mul> is one                                                 */
    if ( EQ(mul,GF2One) ) {
        return AddCoeffsGF2VecGF2Vec( vl, vr );
    }

    /* try next method                                                     */
    return TRY_NEXT_METHOD;
}

/****************************************************************************
**
*F  FuncADDCOEFFS_GF2VEC_GF2VEC_MULT( <self>, <vl>, <vr>, <mul>, <from>, <to> )
**  GF2 vectors
*/
Obj FuncADDCOEFFS_GF2VEC_GF2VEC_MULT_LIMS (
    Obj                 self,
    Obj                 vl,
    Obj                 vr,
    Obj                 mul )
{
    /* do nothing if <mul> is zero                                         */
    if ( EQ(mul,GF2Zero) ) {
        return INTOBJ_INT( RightMostOneGF2Vec(vl) );
    }

    /* add if <mul> is one                                                 */
    if ( EQ(mul,GF2One) ) {
        return AddCoeffsGF2VecGF2Vec( vl, vr );
    }

    /* try next method                                                     */
    return TRY_NEXT_METHOD;
}


/****************************************************************************
**
*F  FuncADDCOEFFS_GF2VEC_GF2VEC( <self>, <vl>, <vr> ) . . . . . . GF2 vectors
*/
Obj FuncADDCOEFFS_GF2VEC_GF2VEC (
    Obj                 self,
    Obj                 vl,
    Obj                 vr )
{
    return AddCoeffsGF2VecGF2Vec( vl, vr );
}


/****************************************************************************
**
*F  FuncSHRINKCOEFFS_GF2VEC( <self>, <vec> )  . . . . . remove trailing zeros
*/
Obj FuncSHRINKCOEFFS_GF2VEC (
    Obj                 self,
    Obj                 vec )
{
    UInt                len;
    UInt                nbb;
    UInt                onbb;
    UInt *              ptr;
    UInt		off;

    /* get length and number of blocks                                     */
    len = LEN_GF2VEC(vec);
    if ( len == 0 ) {
        return INTOBJ_INT(0);
    }

    nbb = ( len + BIPEB - 1 ) / BIPEB;
    onbb = nbb; 
    ptr = BLOCKS_GF2VEC(vec) + (nbb-1);
    
    /* number of insignificant bit positions in last word */
    off = BIPEB - ((len-1)%BIPEB+1); 
    
    /* mask out the last bits */
#ifdef SYS_IS_64_BIT
    *ptr &= 0xffffffffffffffff >> off;
#else
    *ptr &= 0xffffffff >> off;
#endif

    /* find last non-trivial block */
    while ( 0 < nbb && ! *ptr ) {
        nbb--;
        ptr--;
    }
    /* did the block number change? */
    if (nbb < onbb) {
      len = nbb * BIPEB;
    }

    /* find position inside this block                   */
    /* we are guaranteed not to cross a block boundary ! */
    while ( 0 < len && ! ( *ptr & MASK_POS_GF2VEC(len) ) ) {
        len--;
    }
    ResizeWordSizedBag( vec, SIZE_PLEN_GF2VEC(len) );
    SET_LEN_GF2VEC( vec, len );
    return INTOBJ_INT(len);
}

/****************************************************************************
**
*F  FuncPOSITION_NONZERO_GF2VEC( <self>, <vec>, <zero>) ..find first non-zero
**
**  The pointless zero argument is because this is a method for PositionNot
**  It is *not* used in the code and can be replaced by a dummy argument.
*/

UInt PositionNonZeroGF2Vec ( Obj vec, UInt from)
{
    UInt                len;
    UInt                nbb;
    UInt                nb;
    UInt *              ptr;
    UInt                pos;

    /* get length and number of blocks                                     */
    len = LEN_GF2VEC(vec);
    if ( len == 0 ) {
      return 1;
    }


    nbb = from / BIPEB;
    pos = from % BIPEB;
    ptr = BLOCKS_GF2VEC(vec)+nbb;
    if (pos) /* partial block to check */
      {
	pos = from+1;
	while ( (pos - 1)%BIPEB  && pos <= len)
	  {
	    if ((*ptr) & MASK_POS_GF2VEC(pos)) 
	      return (pos);
	    pos++;
	  }
	if (pos > len)
	  return len+1;
	nbb++;
	ptr++;
      }
    /* find first non-trivial block                                         */
    nb = NUMBER_BLOCKS_GF2VEC(vec);
    while ( nbb < nb && ! *ptr ) {
        nbb++;
        ptr++;
    }

    /* find position inside this block                                     */
    pos = nbb * BIPEB + 1;
    while ( pos <= len && ! ( *ptr & MASK_POS_GF2VEC(pos) ) ) {
        pos++;
    }
    /* as the code is intended to run over, trailing 1's are innocent */
    if (pos <= len)
      return pos;
    else
      return len+1;
}


Obj FuncPOSITION_NONZERO_GF2VEC(
    Obj                 self,
    Obj                 vec,
    Obj                 zero)
{
  return INTOBJ_INT(PositionNonZeroGF2Vec(vec, 0));
}

Obj FuncPOSITION_NONZERO_GF2VEC3(
    Obj                 self,
    Obj                 vec,
    Obj                 zero,
    Obj                 from)
{
  return INTOBJ_INT(PositionNonZeroGF2Vec(vec, INT_INTOBJ(from)));
}



Obj FuncCOPY_SECTION_GF2VECS(Obj self, Obj src, Obj dest, Obj from, Obj to, Obj howmany) 
{
  Int   ifrom;
  Int   ito;
  Int   ihowmany;
  UInt  lens;
  UInt  lend;
  if (!IS_GF2VEC_REP(src) ||
      !IS_GF2VEC_REP(dest) ||
      !IS_INTOBJ(from) || 
      !IS_INTOBJ(to) || 
      !IS_INTOBJ(howmany)) 
    ErrorMayQuit("Bad argument types", 0,0);
  ifrom = INT_INTOBJ(from);
  ito = INT_INTOBJ(to);
  ihowmany = INT_INTOBJ(howmany);
  lens = LEN_GF2VEC(src);
  lend = LEN_GF2VEC(dest);
  if (ifrom <= 0 || ito <= 0 ||
      ihowmany < 0 || ifrom + ihowmany -1 > lens || ito + ihowmany -1 > lend)
    ErrorMayQuit("Bad argument values",0,0);
  if (!IS_MUTABLE_OBJ(dest))
    ErrorMayQuit("Immutable destination vector", 0,0);
  CopySection_GF2Vecs(src, dest, (UInt)ifrom, (UInt)ito, (UInt)ihowmany);
  return (Obj) 0;
}


/****************************************************************************
**
*F  FuncAPPEND_GF2VEC( <self>, <vecl>, <vecr> )
**
*/

Obj FuncAPPEND_GF2VEC( Obj self, Obj vecl, Obj vecr )
{
  UInt lenl, lenr;
  lenl = LEN_GF2VEC(vecl);
  lenr = LEN_GF2VEC(vecr);
  if (True == DoFilter(IsLockedRepresentationVector, vecl) && lenr > 0)
    {
      ErrorMayQuit("Append to locked compressed vector is forbidden", 0, 0);
      return 0;
    }
  ResizeWordSizedBag(vecl, SIZE_PLEN_GF2VEC(lenl+lenr));
  CopySection_GF2Vecs(vecr, vecl, 1, lenl+1, lenr);
  SET_LEN_GF2VEC(vecl,lenl+lenr);
  return (Obj) 0;
}


/****************************************************************************
**
*F  FuncSHALLOWCOPY_GF2VEC( <self>, <vec> )
**
*/

Obj FuncSHALLOWCOPY_GF2VEC( Obj self, Obj vec)
{
  return ShallowCopyVecGF2(vec);
}

/****************************************************************************
**
*F  FuncSUM_GF2MAT_GF2MAT( <self>, <matl>, <matr> )
**
*/


Obj FuncSUM_GF2MAT_GF2MAT( Obj self, Obj matl, Obj matr)
{
  UInt ll,lr,ls, lm,wl,wr,ws,wm;
  Obj sum;
  Obj vl,vr, sv;
  UInt i;
  Obj rtype;
  ll = LEN_GF2MAT(matl);
  lr = LEN_GF2MAT(matr);
  if (ll > lr)
    {
      ls = ll;
      lm = lr;
    }
  else
    {
      ls = lr;
      lm = ll;
    }
  wl = LEN_GF2VEC(ELM_GF2MAT(matl, 1));
  wr = LEN_GF2VEC(ELM_GF2MAT(matr, 1));
  if (wl > wr)
    {
      ws = wl;
      wm = wr;
    }
  else
    {
      ws = wr;
      wm = wl;
    }

  /* In this case, the result is not rectangular */
  
  if ((ll > lr && wr > wl) || (ll < lr && wr < wl)) 
    return TRY_NEXT_METHOD;

  
  sum = NewBag(T_POSOBJ, SIZE_PLEN_GF2MAT( ls ));
  if (IS_MUTABLE_OBJ(matl) || IS_MUTABLE_OBJ(matr))
    {
      SET_TYPE_POSOBJ(sum, TYPE_LIST_GF2MAT);
      if (IS_MUTABLE_OBJ(ELM_GF2MAT(matl,1)) || IS_MUTABLE_OBJ(ELM_GF2MAT(matr,1)))
	rtype = TYPE_LIST_GF2VEC_LOCKED;
      else
	rtype = TYPE_LIST_GF2VEC_IMM_LOCKED;
    }
  else
    {
      SET_TYPE_POSOBJ(sum, TYPE_LIST_GF2MAT_IMM);
      rtype = TYPE_LIST_GF2VEC_IMM_LOCKED;
    }
  
  SET_LEN_GF2MAT(sum, ls);
  for (i = 1; i <= lm; i++)
    {

      /* copy the longer vector and add the shorter */
      if (wl == ws) 
	{
	  sv = ShallowCopyVecGF2( ELM_GF2MAT( matl, i));
	  vr = ELM_GF2MAT( matr, i);
	  AddGF2VecToGF2Vec(BLOCKS_GF2VEC(sv), BLOCKS_GF2VEC(vr), wm);
	}
      else
	{
	  sv = ShallowCopyVecGF2( ELM_GF2MAT(matr, i));
	  vl = ELM_GF2MAT( matl, i);
	  AddGF2VecToGF2Vec(BLOCKS_GF2VEC(sv), BLOCKS_GF2VEC(vl), wm);
	}
      
      SetTypeDatObj(sv, rtype);
      SET_ELM_GF2MAT( sum, i, sv);
      CHANGED_BAG(sum);
    }
  for (; i <= ls; i++)
    {
      if (ll > lr)
	sv = ELM_GF2MAT(matl, i);
      else
	sv = ELM_GF2MAT(matr, i);

      if (rtype == TYPE_LIST_GF2VEC_LOCKED)
	sv = ShallowCopyVecGF2( sv );
      
      SetTypeDatObj(sv, rtype);
      SET_ELM_GF2MAT( sum, i, sv);
      CHANGED_BAG(sum);      
    }
  return sum;
}


/****************************************************************************
**
*F  FuncTRANSPOSED_GF2MAT( <self>, <mat>)
**
*/
Obj FuncTRANSPOSED_GF2MAT( Obj self, Obj mat)
{
  UInt l,w;
  Obj tra,row;
  Obj typ,r1;
  UInt vals[BIPEB];
  UInt mask,val,bit;
  UInt imod,nrb,nstart;
  UInt i,j,k,n;
  UInt * ptr;

  /* check argument */
  if (TNUM_OBJ(mat) != T_POSOBJ) {
     mat = ErrorReturnObj(
            "TRANSPOSED_GF2MAT: Need compressed matrix over GF(2)\n",
            0, 0, 
            "You can return such matrix with 'return mat;'\n");
  }
  /* type for mat */
  typ = TYPE_LIST_GF2MAT;


  /* we assume here that there is a first row */
  r1 = ELM_GF2MAT(mat,1);
  
  l = LEN_GF2MAT(mat);
  w = LEN_GF2VEC(r1);
  nrb=NUMBER_BLOCKS_GF2VEC(r1);

  tra = NewBag(T_POSOBJ, SIZE_PLEN_GF2MAT( w ));
  SET_TYPE_POSOBJ(tra, typ);

  /* type for rows */
  typ = TYPE_LIST_GF2VEC_LOCKED;
  
  SET_LEN_GF2MAT(tra, w);
  /* create new matrix */
  for (i = 1; i <= w; i++) {
    NEW_GF2VEC( row,  typ, l );

    ptr=BLOCKS_GF2VEC(row);
    for (n=1;n<=nrb;n++){*ptr=0;}

    SET_ELM_GF2MAT( tra, i, row);
    CHANGED_BAG(tra);
  }
  /* set entries */
  /* run over BIPEB row chunks of the original matrix */
  for (i = 1; i <= l; i=i+BIPEB) {
    imod=(i-1)/BIPEB;
    /* run through these rows in block chunks */
    for (n=0;n<nrb;n++) {
      for (j=0;j<BIPEB;j++) {
	if ((i+j)>l) {
	  vals[j]=0; /* outside matrix */ 
	}
	else {
	  ptr=BLOCKS_GF2VEC(ELM_GF2MAT(mat,i+j))+n;
	  vals[j]=*ptr;
	}
      }
      /* write transposed values in new matrix */
      mask=1;
      nstart=n*BIPEB+1;
      for (j=0;j<BIPEB;j++) { /* bit number = Row in transpose */
	if ((nstart+j)<=w) {
	  /* still within matrix */
	  val=0;
	  bit=1;
	  for (k=0;k<BIPEB;k++) {
	    if (mask==(vals[k]&mask)) {
	      val|=bit; /* set bit */
	    }
	    bit=bit<<1;
	  }
	  /* set entry */
	  ptr=BLOCKS_GF2VEC(ELM_GF2MAT(tra,nstart+j))+imod;
	  *ptr=val;
	  /* next bit */
	  mask=mask<<1;
	}
      }
    }
  }
  return tra;
}


/****************************************************************************
**
*F  FuncNUMBER_GF2VEC( <self>, <vect> )
**
*/
Obj FuncNUMBER_GF2VEC( Obj self, Obj vec )
{
  UInt len,nd,i;
  UInt head,a;
  UInt off,off2;		/* 0 based */
  Obj zahl;  /* the long number */
  UInt *num;
  mp_limb_t *vp;
  len = LEN_GF2VEC(vec);
  if (len == 0)
    return INTOBJ_INT(1);
  num = BLOCKS_GF2VEC(vec) + (len-1)/BIPEB;
  off = (len -1) % BIPEB + 1; /* number of significant bits in last word */
  off2 = BIPEB - off;         /* number of insignificant bits in last word */

  /* mask out the last bits */
#ifdef SYS_IS_64_BIT
  *num &= 0xffffffffffffffff >> off2;
#else
  *num &= 0xffffffff >> off2;
#endif

  if (len <=NR_SMALL_INT_BITS) 
    /* it still fits into a small integer */
    return INTOBJ_INT(revertbits(*num,len));
  else {
    /* we might have to build a long integer */

    /* the number of words (limbs) we need. */
    nd = ((len-1)/GMP_LIMB_BITS)+1;

    zahl = NewBag( T_INTPOS, nd*sizeof(UInt) );
    /*    zahl = NewBag( T_INTPOS, (((nd+1)>>1)<<1)*sizeof(UInt) );*/
    /* +1)>>1)<<1: round up to next even number*/

    /* garbage collection might lose pointer */
    num = BLOCKS_GF2VEC(vec) + (len-1)/BIPEB;

    vp = (mp_limb_t *)ADDR_OBJ(zahl); /* the place we write to */
    i=1;

    if (off!=BIPEB) {
      head = revertbits(*num,off); /* the last 'off' bits, reverted */
      while (i<nd) {
	/* next word */
	num--;
	*vp = head; /* the bits left from last word */
	a = revertbits(*num,BIPEB); /* the full word reverted */
	head = a>>off2; /* next head: trailing `off' bits */
	a =a << off; /* the rest of the word */
	*vp |=a;
	vp++;
	i++;
      }
      *vp = head; /* last head bits */
      vp++;
    }
    else {
      while (i<=nd) {
        *vp=revertbits(*num--,BIPEB);
	vp++;
	i++;
      }
    }


    zahl = GMP_NORMALIZE(zahl);
    zahl = GMP_REDUCE(zahl);
    
    return zahl;
  }

}

/****************************************************************************
**
*F  FuncLT_GF2VEC_GF2VEC( <self>, <vl>, <vr> )   compare GF2 vectors
*/
Obj FuncLT_GF2VEC_GF2VEC (
    Obj                 self,
    Obj                 vl,
    Obj                 vr )
{
  return (Cmp_GF2VEC_GF2VEC(vl,vr) < 0) ? True : False;
}

/****************************************************************************
**
*F  Cmp_GF2MAT_GF2MAT( <ml>, <mr> )   compare GF2 matrices
*/

Int Cmp_GF2MAT_GF2MAT( Obj ml, Obj mr)
{
  UInt l1, l2,l, i;
  Int c;
  l1 = INT_INTOBJ(ELM_PLIST(ml,1));
  l2 = INT_INTOBJ(ELM_PLIST(mr,1));
  l = (l1 < l2) ? l1 : l2;
  for (i = 2; i <= l+1; i++)
    {
      c = Cmp_GF2VEC_GF2VEC(ELM_PLIST(ml,i), ELM_PLIST(mr,i));
      if (c != 0)
	return c;
    }
  if (l1 < l2)
    return -1;
  if (l1 > l2)
    return 1;
  return 0;
  
}

/****************************************************************************
**
*F  FuncEQ_GF2MAT_GF2MAT( <ml>, <mr> )   compare GF2 matrices
*/

Obj FuncEQ_GF2MAT_GF2MAT( Obj self, Obj ml, Obj mr)
{
  if (ELM_PLIST(ml,1) != ELM_PLIST(mr,1))
    return False;
  return (0 == Cmp_GF2MAT_GF2MAT(ml,mr)) ? True : False;
}

/****************************************************************************
**
*F  FuncLT_GF2MAT_GF2MAT( <ml>, <mr> )   compare GF2 matrices
*/

Obj FuncLT_GF2MAT_GF2MAT( Obj self, Obj ml, Obj mr)
{
  return (Cmp_GF2MAT_GF2MAT(ml,mr) < 0) ? True : False;
}

/****************************************************************************
**
*F  DistGF2Vecs( <ptL>, <ptR>, <len> )
**
**  computes the GF2-vector distance of two blocks in memory, pointed to by
**  ptL and ptR for a GF(2) vector of <len> entries.
**  
*/
UInt DistGF2Vecs(UInt* ptL,UInt* ptR,UInt len)
{
  UInt 			sum,m;
  UInt *                end;            /* end marker                      */

  /*T this  function will not work if the vectors have more than 2^28
   * entries */

  end = ptL + ((len+BIPEB-1)/BIPEB);
  sum=0;
  /* loop over the entries */
  /*T possibly unroll this loop */
  while ( ptL < end ) {
    m = *ptL++ ^ *ptR++; /* xor of bits, nr bits therein is difference */
    sum += COUNT_TRUES_BLOCK(m);
  }
  return sum;
}

/****************************************************************************
**
*F  FuncDIST_GF2VEC_GF2VEC( <self>, <vl>, <vr> )
**
**  'FuncDIST_GF2VEC_GF2VEC' returns the number of position in which two
**  gf2-vectors <vl>  and  <vr> differ.                            
*/
Obj FuncDIST_GF2VEC_GF2VEC (
    Obj                 self,
    Obj                 vl,
    Obj                 vr )
{
  UInt                  len;            /* length of the list              */
  UInt                  off;            /* bit offset at the end to clean out */
  UInt *                ptL;            /* bit field of <vl>               */
  UInt *                ptR;            /* bit field of <vr>               */
  UInt *                end;            /* pointer used to zero out end bit */
  /* get and check the length                                            */
  len = LEN_GF2VEC(vl);

  if ( len != LEN_GF2VEC(vr) ) {
    ErrorMayQuit(
      "DIST_GF2VEC_GF2VEC: vectors must have the same length",0L,0L);
    return 0;
  }

  /* calculate the offsets */
  ptL = BLOCKS_GF2VEC(vl);
  ptR = BLOCKS_GF2VEC(vr);

/* mask out the last bits */
  off = (len -1) % BIPEB + 1; /* number of significant bits in last word */
  off = BIPEB - off;          /* number of insignificant bits in last word */
  end = ptL + ((len-1)/BIPEB);
#ifdef SYS_IS_64_BIT
  *end &= 0xffffffffffffffff >> off;
#else
  *end &= 0xffffffff >> off;
#endif
  end = ptR + ((len-1)/BIPEB);
#ifdef SYS_IS_64_BIT
  *end &= 0xffffffffffffffff >> off;
#else
  *end &= 0xffffffff >> off;
#endif

  return INTOBJ_INT(DistGF2Vecs(ptL,ptR,len));
}


Int DistVecClosVec(
  Obj		veclis, /* pointers to matrix vectors and their multiples */
  Obj 	        ovec,    /* vector we compute distance to */
  Obj		d,	/* distances list */
  Obj  	        osum,	/* position of the sum vector */
  UInt		pos,	/* recursion depth */
  UInt		l,	/* length of basis */
  UInt		len )	/* length of the involved vectors */
{
  UInt 		i;
  UInt		di;
  Obj		cnt;
  Obj		vp;
  UInt *        vec;
  UInt *        sum;
  Obj           one;
  Obj           tmp;
  Int		chg;

  vec = BLOCKS_GF2VEC(ovec);
  sum = BLOCKS_GF2VEC(osum);
  vp = ELM_PLIST(veclis,pos);
  one = INTOBJ_INT(1);
  chg=0;
  
  for (i=0;i<=1;i++) {
    if (pos < l)
      {
	chg |= DistVecClosVec(veclis,ovec,d,osum,pos+1,l,len);
	if (chg!=0) {
	  sum = BLOCKS_GF2VEC(osum);
	  }
      }
    else
      {
	di=DistGF2Vecs(sum,vec,len);

	cnt=ELM_PLIST(d,di+1);
	if (IS_INTOBJ(cnt) && SUM_INTOBJS(tmp, cnt, one))
	  {
	    cnt = tmp;
	    SET_ELM_PLIST(d,di+1,cnt);
	  }
	else
	  {
	    cnt=SumInt(cnt,one);
	    vec = BLOCKS_GF2VEC(ovec);
	    sum = BLOCKS_GF2VEC(osum);
	    SET_ELM_PLIST(d,di+1,cnt);
	    CHANGED_BAG(d);
	    chg=1;
	  }
      }
    AddGF2VecToGF2Vec(sum,BLOCKS_GF2VEC(ELM_PLIST(vp,i+1)),len);
  }
  return chg;
}

Obj FuncDIST_VEC_CLOS_VEC(
  Obj		self,
  Obj		veclis, /* pointers to matrix vectors and their multiples */
  Obj		vec,    /* vector we compute distance to */
  Obj		d )	/* distances list */

{
  Obj		sum; /* sum vector */
  UInt 		len;

  len = LEN_GF2VEC(vec);

  /* get space for sum vector */
  NEW_GF2VEC( sum, TYPE_LIST_GF2VEC, len );

  /* do the recursive work */
  DistVecClosVec(veclis,vec,d,sum,1,LEN_PLIST(veclis),len);

  return (Obj) 0;
}

UInt AClosVec(
  Obj		veclis, /* pointers to matrix vectors and their multiples */
  Obj 	        ovec,    /* vector we compute distance to */
  Obj  	        osum,	/* position of the sum vector */
  UInt		pos,	/* recursion depth */
  UInt		l,	/* length of basis */
  UInt		len,	/* length of the involved vectors */
  UInt		cnt,	/* numbr of vectors used */
  UInt		stop,	/* stop value */
  UInt		bd,	/* best distance so far */
  Obj		obv,    /* best vector so far */
  Obj           coords,  /* coefficients to get current vector */
  Obj           bcoords  /* coefficients to get best vector */
  )	
{
  UInt		di;
  Obj		vp;
  UInt *        vec;
  UInt *        sum;
  UInt *        bv;
  UInt *        end;
  UInt *w;



  /* maybe we don't add this basis vector -- if this leaves us enough possibilitiies */
  if ( pos+cnt<l ) {
    bd = AClosVec(veclis,ovec,osum,pos+1,l,len,cnt,stop,bd,obv,coords,bcoords);
      if (bd<=stop) {
        return bd;
      }
  }


  /* Otherwise we do */
  
  vec = BLOCKS_GF2VEC(ovec);
  sum = BLOCKS_GF2VEC(osum);
  vp = ELM_PLIST(veclis,pos);
  w = BLOCKS_GF2VEC(ELM_PLIST(vp,1));
  AddGF2VecToGF2Vec(sum,w,len);

  if (coords != (Obj) 0)
    {
      SET_ELM_PLIST(coords,pos,INTOBJ_INT(1));
    }

  
  if (cnt == 0) /* this is a candidate */
    {
      di=DistGF2Vecs(sum,vec,len);
      if (di<bd) {
	
	/* store new result */
	bd=di;
	bv = BLOCKS_GF2VEC(obv);
	end = bv+((len+BIPEB-1)/BIPEB);
	while (bv<end) 
	  *bv++=*sum++;
	sum = BLOCKS_GF2VEC(osum);
	if (coords != (Obj) 0)
	  {
	    UInt i;
	    for (i=1; i <= l; i++)
	      {
		Obj x;
		x = ELM_PLIST(coords,i);
		SET_ELM_PLIST(bcoords,i,x);
	      }
	  }
      }
    }
  else /* need to add in some more */
    {
      bd=AClosVec(veclis,ovec,osum,pos+1,l,len,cnt-1,stop,bd,obv,coords,bcoords);
      if (bd<=stop) {
	return bd;
      }
    }
    
  /* reset component  */
  AddGF2VecToGF2Vec(sum,w,len);
  if (coords != (Obj) 0)
    {
      SET_ELM_PLIST(coords,pos,INTOBJ_INT(0));
    }
  
  TakeInterrupt();
  return bd;
}





Obj FuncA_CLOS_VEC(
  Obj		self,
  Obj		veclis, /* pointers to matrix vectors and their multiples */
  Obj		vec,    /* vector we compute distance to */
  Obj		cnt,	/* distances list */
  Obj		stop)	/* distances list */

{
  Obj		sum; /* sum vector */
  Obj		best; /* best vector */
  UInt 		len;

  len = LEN_GF2VEC(vec);

  if (!ARE_INTOBJS(cnt,stop))
    ErrorMayQuit("AClosVec: cnt and stop must be small integers, not a %s and a %s",
	      (Int)TNAM_OBJ(cnt), (Int)TNAM_OBJ(stop));
  

  /* get space for sum vector and zero out */
  NEW_GF2VEC( sum, TYPE_LIST_GF2VEC, len );
  NEW_GF2VEC( best, TYPE_LIST_GF2VEC, len );

  /* do the recursive work */
  AClosVec(veclis,vec,sum,1, LEN_PLIST(veclis),len,
    INT_INTOBJ(cnt),INT_INTOBJ(stop),len+1, /* maximal value +1 */
	   best, (Obj) 0, (Obj) 0);

  return best;
}

Obj FuncA_CLOS_VEC_COORDS(
  Obj		self,
  Obj		veclis, /* pointers to matrix vectors and their multiples */
  Obj		vec,    /* vector we compute distance to */
  Obj		cnt,	/* distances list */
  Obj		stop)	/* distances list */

{
  Obj		sum; /* sum vector */
  Obj		best; /* best vector */
  Obj         coords; /* coefficients of mat to get current */
  Obj         bcoords; /* coefficients of mat to get best */
  Obj           res; /* length 2 plist for results */
  UInt 		len, len2,i;

  len = LEN_GF2VEC(vec);
  len2 = LEN_PLIST(veclis);

  if (!ARE_INTOBJS(cnt,stop))
    ErrorMayQuit("AClosVec: cnt and stop must be small integers, not a %s and a %s",
	      (Int)TNAM_OBJ(cnt), (Int)TNAM_OBJ(stop));
  

  /* get space for sum vector and zero out */
  NEW_GF2VEC( sum, TYPE_LIST_GF2VEC, len );
  NEW_GF2VEC( best, TYPE_LIST_GF2VEC, len );

  coords = NEW_PLIST(  T_PLIST_CYC, len2 );
  SET_LEN_PLIST( coords, len2 );

  bcoords = NEW_PLIST(  T_PLIST_CYC, len2 );
  SET_LEN_PLIST( bcoords, len2 );
  
  for (i=1; i <= len2; i++)
    {
      SET_ELM_PLIST(coords,i,INTOBJ_INT(0));
      SET_ELM_PLIST(bcoords,i,INTOBJ_INT(0));
    }

  /* do the recursive work */
  AClosVec(veclis,vec,sum,1, len2 ,len,
    INT_INTOBJ(cnt),INT_INTOBJ(stop),len+1, /* maximal value +1 */
	   best,coords,bcoords);

  res = NEW_PLIST(T_PLIST_DENSE_NHOM,2);
  SET_LEN_PLIST(res,2);
  SET_ELM_PLIST(res,1,best);
  SET_ELM_PLIST(res,2,bcoords);
  CHANGED_BAG(res);
  return res;
}

/****************************************************************************
**
*F FuncCOSET_LEADERS_INNER_GF2( <self>, <veclis>, <weight>, <tofind>, <leaders> )
**
** Search for new coset leaders of weight <weight>
*/

UInt CosetLeadersInnerGF2( Obj veclis,
			   Obj v,
			   Obj w,
			   UInt weight,
			   UInt pos,
			   Obj leaders,
			   UInt tofind )
{
  UInt found = 0;
  UInt len = LEN_GF2VEC(v);
  UInt lenw = LEN_GF2VEC(w);
  UInt sy;
  UInt u0;
  Obj vc;
  UInt i,j;
    
  /* we know that the length of w does not exceed BIPEB -4 here
     (or there would not be room in a PLIST for all the coset leaders)
    we use this to do a lot of GF2 vector operations for w "in-place"

    Even more in this direction could be done, but this no longer
  the rate-determining step for any feasible application*/
  
  if (weight == 1)
    {
      for (i = pos; i <= len; i++)
	{
	  u0 = BLOCKS_GF2VEC(ELM_PLIST(ELM_PLIST(veclis, i),1))[0];
	  BLOCKS_GF2VEC(w)[0] ^= u0;
	  BLOCK_ELM_GF2VEC(v, i) |= MASK_POS_GF2VEC(i);

	  sy = revertbits(BLOCKS_GF2VEC(w)[0], lenw);
	  if ((Obj) 0 == ELM_PLIST(leaders,sy+1))
	    {
	      NEW_GF2VEC(vc, TYPE_LIST_GF2VEC_IMM, len);
	      for (j = 0; j < NUMBER_BLOCKS_GF2VEC(v); j++)
		BLOCKS_GF2VEC(vc)[j] = BLOCKS_GF2VEC(v)[j];
	      SET_ELM_PLIST(leaders,sy+1,vc);
	      CHANGED_BAG(leaders);
	      if (++found == tofind)
		return found;
	    }
	  BLOCKS_GF2VEC(w)[0] ^= u0;
	  BLOCK_ELM_GF2VEC(v, i) &= ~MASK_POS_GF2VEC(i);
	}
    }
  else
    {
      if (pos + weight <= len)
	{
	  found += CosetLeadersInnerGF2(veclis, v, w, weight, pos+1, leaders, tofind);
	  if (found == tofind)
	    return found;
	}
      u0 = BLOCKS_GF2VEC(ELM_PLIST(ELM_PLIST(veclis, pos),1))[0];
      BLOCKS_GF2VEC(w)[0] ^= u0;
      BLOCK_ELM_GF2VEC(v, pos) |= MASK_POS_GF2VEC(pos);
      found += CosetLeadersInnerGF2(veclis, v, w, weight -1, pos + 1, leaders, tofind - found);
      if (found == tofind)
	return found;
      BLOCKS_GF2VEC(w)[0] ^= u0;
      BLOCK_ELM_GF2VEC(v, pos) &= ~MASK_POS_GF2VEC(pos);
    }
  TakeInterrupt();
  return found;
}




Obj FuncCOSET_LEADERS_INNER_GF2( Obj self, Obj veclis, Obj weight, Obj tofind, Obj leaders)
{
  Obj v,w;
  UInt lenv, lenw;

  if (!ARE_INTOBJS(weight,tofind))
    ErrorMayQuit("COSET_LEADERS_INNER_GF2: weight and tofind must be smal integers, not a %s and a %s",
	      (Int)TNAM_OBJ(weight), (Int)TNAM_OBJ(tofind));
  
  lenv = LEN_PLIST(veclis);
  NEW_GF2VEC(v, TYPE_LIST_GF2VEC, lenv);
  lenw = LEN_GF2VEC(ELM_PLIST(ELM_PLIST(veclis,1),1));
  NEW_GF2VEC(w, TYPE_LIST_GF2VEC, lenw);
  if (lenw > BIPEB-4)
    ErrorMayQuit("COSET_LEADERS_INNER_GF2: too many cosets to return the leaders in a plain list",0,0);
  return INTOBJ_INT(CosetLeadersInnerGF2( veclis, v, w, INT_INTOBJ(weight), 1, leaders, INT_INTOBJ(tofind)));
}

/****************************************************************************
**
*F  Polynomial Arithmetic Support
*/


/****************************************************************************
**
*F  FuncRIGHTMOST_NONZERO_GF2VEC (<self>, <vec> )
**
*/

Obj FuncRIGHTMOST_NONZERO_GF2VEC( Obj self, Obj vec )
{
  return INTOBJ_INT(RightMostOneGF2Vec(vec));
}

/****************************************************************************
**
*F  ResizeGF2Vec( <vec>, <newlen>, <clean> )
**
*/

void ResizeGF2Vec( Obj vec, UInt newlen )
{
  UInt len;
  UInt *ptr;
  UInt *nptr;
  UInt off;
  len = LEN_GF2VEC(vec);
  if (len == newlen)
    return;
  if (True == DoFilter(IsLockedRepresentationVector, vec))
    {
      ErrorReturnVoid("Resize of locked compressed vector is forbidden", 0, 0,
		      "You can `return;' to ignore the operation");
      return;
    }

  
  if (newlen > len)
    {
      ResizeWordSizedBag(vec,SIZE_PLEN_GF2VEC(newlen));

      /* now clean remainder of last block */
      if (len == 0)
	ptr = BLOCKS_GF2VEC(vec);
      else
	{
	  ptr = BLOCKS_GF2VEC(vec) + (len -1)/BIPEB;
	  off = BIPEB - ((len -1)% BIPEB + 1); /* number of insignificant bits in last word */
#ifdef SYS_IS_64_BIT
	  *ptr &= 0xffffffffffffffff >> off;
#else
	  *ptr &= 0xffffffff >> off;
#endif
	  ptr++;
	}
      
      /* and clean new blocks -- shouldn't need to do this, but
	 it's very cheap */
      /* newlen can't be zero here, since it is bigger than len */
      nptr = BLOCKS_GF2VEC(vec) + (newlen -1)/BIPEB; 
      while (ptr <= nptr)
	*ptr++ = 0;

      SET_LEN_GF2VEC(vec, newlen);
      return;
    }
  else
    {
      /* clean remainder of new last block, if any */
      if (newlen % BIPEB)
	{
	  ptr = BLOCKS_GF2VEC(vec) + (newlen -1)/BIPEB;
	  off = BIPEB - ((newlen-1) % BIPEB + 1);
#ifdef SYS_IS_64_BIT
	  *ptr &= 0xffffffffffffffff >> off;
#else
	  *ptr &= 0xffffffff >> off;
#endif
	}
      SET_LEN_GF2VEC(vec, newlen);
      ResizeWordSizedBag(vec, SIZE_PLEN_GF2VEC(newlen));
      return;
    }
}

/****************************************************************************
**
*F  FuncRESIZE_GF2VEC( <self>, <vec>, <newlen> )
**
*/

Obj FuncRESIZE_GF2VEC( Obj self, Obj vec, Obj newlen)
{
  Int newlen1;
  if (!IS_MUTABLE_OBJ(vec))
    {
      ErrorReturnVoid("RESIZE_GF2VEC: the vector must be mutable", 0, 0,
		      "you may 'return;' to skip the operation");
      return (Obj)0;
    }
  if (!IS_INTOBJ(newlen))
    ErrorMayQuit("RESIZE_GF2VEC: newlen must be a small integer, not a %s",
	      (Int)TNAM_OBJ(newlen), 0L);
  newlen1 = INT_INTOBJ(newlen);
  if (newlen1 < 0) 
    ErrorMayQuit("RESIZE_GF2VEC: the new size must be a non-negative integer, not %d", newlen1, 0);
  ResizeGF2Vec(vec, newlen1);
  return (Obj)0;
}


/****************************************************************************
**
*F  ShiftLeftGF2Vec( <vec>, <amount> )
**
*/

void ShiftLeftGF2Vec( Obj vec, UInt amount )
{
  UInt len;
  UInt *ptr1, *ptr2;
  UInt i;
  UInt block;
  UInt off;
  if (amount == 0)
    return;
  len = LEN_GF2VEC(vec);
  if (amount >= len)
    {
      ResizeGF2Vec(vec, 0);
      return;
    }
  if (amount % BIPEB == 0)
    {
      ptr1 = BLOCKS_GF2VEC(vec);
      ptr2 = ptr1 + amount/BIPEB;
      for (i = 0; i < (len - amount + BIPEB - 1)/BIPEB; i++)
	*ptr1++ = *ptr2++;
    }
  else
    {
      ptr1 = BLOCKS_GF2VEC(vec);
      ptr2 = ptr1 + amount/BIPEB;
      off = amount % BIPEB;
      for (i = 0; i < (len - amount + BIPEB - 1)/BIPEB - 1; i++)
      {
        block = (*ptr2++) >> off;
        block |= (*ptr2) << (BIPEB - off);
        *ptr1++ = block;
      }
      // Handle last block seperately to avoid reading off end of Bag
      block = (*ptr2++) >> off;
      if(ptr2 < BLOCKS_GF2VEC(vec) + NUMBER_BLOCKS_GF2VEC(vec))
        block |= (*ptr2) << (BIPEB - off);
      *ptr1 = block;
    }
  ResizeGF2Vec(vec, len-amount);
}

/****************************************************************************
**
*F  FuncSHIFT_LEFT_GF2VEC(<self>, <vec>, <amount> )
**
*/

Obj FuncSHIFT_LEFT_GF2VEC( Obj self, Obj vec, Obj amount)
{
  Int amount1;
  if (!IS_MUTABLE_OBJ(vec)) 
    {
      ErrorReturnVoid("SHIFT_LEFT_GF2VEC: the vector must be mutable", 0, 0,
		      "you may 'return;' to skip the operation");
      return (Obj)0;
    }
  if (!IS_INTOBJ(amount))
    ErrorMayQuit("SHIFT_LEFT_GF2VEC: the amnount to shift must be a small integer, not a %d",
	      (Int)TNAM_OBJ(amount), 0L);
  amount1 = INT_INTOBJ(amount);
  if (amount1 < 0)
     ErrorMayQuit("SHIFT_LEFT_GF2VEC: <amount> must be a non-negative integer, not %d", amount1, 0);
  ShiftLeftGF2Vec(vec, amount1);
  return (Obj)0;
}

/****************************************************************************
**
*F  ShiftRightGF2Vec( <vec>, <amount> )
**
*/

void ShiftRightGF2Vec( Obj vec, UInt amount )
{
  UInt len;
  UInt *ptr1, *ptr2, *ptr0;
  UInt i;
  UInt block;
  UInt off;
  if (amount == 0)
    return;
  len = LEN_GF2VEC(vec);
  ResizeGF2Vec(vec, len+amount);
  if (amount % BIPEB == 0)
    {
      /* move the blocks */
      ptr1 = BLOCKS_GF2VEC(vec) + (len - 1 + amount)/BIPEB;
      ptr2 = ptr1 - amount/BIPEB;
      for (i = 0; i < (len + BIPEB - 1)/BIPEB; i++)
	*ptr1-- = *ptr2--;

      /* and fill with zeroes */
      ptr2 = BLOCKS_GF2VEC(vec);
      while (ptr1 >= ptr2)
	*ptr1-- = 0;
    }
  else
    {
      ptr1 = BLOCKS_GF2VEC(vec) + (len -1 + amount)/BIPEB;
      ptr2 = ptr1 - amount/BIPEB; /* this can sometimes be the block AFTER the old last block,
				     but this must be OK */
      off = amount % BIPEB;
      ptr0 = BLOCKS_GF2VEC(vec);
      while (1)
	{
	  block = (*ptr2--) << off;
	  if (ptr2 < ptr0)
	    break;
	  block |= (*ptr2) >> (BIPEB - off);
	  *ptr1-- = block;
	}
      *ptr1-- = block;
      while (ptr1 >= ptr0)
	*ptr1-- = 0;
    }
}

/****************************************************************************
**
*F  FuncSHIFT_RIGHT_GF2VEC(<self>, <vec>, <amount> )
**
*/

Obj FuncSHIFT_RIGHT_GF2VEC( Obj self, Obj vec, Obj amount)
{
  Int amount1;
  if (!IS_MUTABLE_OBJ(vec))
    {
      ErrorReturnVoid("SHIFT_RIGHT_GF2VEC: the vector must be mutable", 0, 0,
		      "you may 'return;' to skip the operation");
      return (Obj)0;
    }
  if (!IS_INTOBJ(amount))
    ErrorMayQuit("SHIFT_RIGHT_GF2VEC: the amount to shift must be a small integer, not a %s",
	      (Int)TNAM_OBJ(amount), 0L);
  amount1 = INT_INTOBJ(amount);
  if (amount1 < 0)
      ErrorMayQuit("SHIFT_RIGHT_GF2VEC: <amount> must be a non-negative integer, not %d", amount1, 0);
  ShiftRightGF2Vec(vec, amount1);
  return (Obj)0;
}

/* ReduceCoeffs */

/****************************************************************************
**
*F  AddShiftedVecGF2VecGF2( <vec1>, <vec2>, <len2>, <off> )
**
*/

void AddShiftedVecGF2VecGF2( Obj vec1, Obj vec2, UInt len2, UInt off )
{
  UInt *ptr1, *ptr2;
  UInt i;
  UInt block;
  UInt shift1, shift2;
  if (off % BIPEB == 0)
    {
      ptr1 = BLOCKS_GF2VEC(vec1) + off/BIPEB;
      ptr2 = BLOCKS_GF2VEC(vec2);
      for (i = 0; i < (len2 - 1)/BIPEB; i++)
	*ptr1++ ^= *ptr2++;
      block = *ptr2;
#ifdef SYS_IS_64_BIT
      block &= (0xFFFFFFFFFFFFFFFF >> (BIPEB - (len2-1) % BIPEB -1));
#else
      block &= (0xFFFFFFFF >> (BIPEB - (len2-1) % BIPEB-1));
#endif
      *ptr1 ^= block;
    }
  else
    {
      ptr1 = BLOCKS_GF2VEC(vec1) + off/BIPEB;
      ptr2 = BLOCKS_GF2VEC(vec2);
      shift1 = off %BIPEB;
      shift2 = BIPEB - off%BIPEB;
      for (i = 0; i < len2/BIPEB; i++)
	{
	  *ptr1++ ^= *ptr2 << shift1;
	  *ptr1   ^= *ptr2++ >> shift2;
	}

      if (len2 % BIPEB)
	{
	  block = *ptr2;
#ifdef SYS_IS_64_BIT
	  block &= 0xFFFFFFFFFFFFFFFF >> (BIPEB - (len2-1) % BIPEB-1);
#else
	  block &= 0xFFFFFFFF >> (BIPEB - (len2-1) % BIPEB-1);
#endif
	  *ptr1++ ^= block << shift1;
	  if (len2 % BIPEB + off % BIPEB > BIPEB)
	    {
	      assert(ptr1 < BLOCKS_GF2VEC(vec1) + (LEN_GF2VEC(vec1) + BIPEB -1)/BIPEB);
	      *ptr1 ^= block >> shift2;
	    }
	}
    }
}

/****************************************************************************
**
*F FuncADD_GF2VEC_GF2VEC_SHIFTED( <self>, <vec1>, <vec2>, <len2>, <off> )
**
*/

Obj FuncADD_GF2VEC_GF2VEC_SHIFTED( Obj self, Obj vec1, Obj vec2, Obj len2, Obj off)
{
  Int off1, len2a;
  if (!IS_INTOBJ(off))
    ErrorMayQuit("ADD_GF2VEC_GF2VEC_SHIFTED: offset should be a small integer not a %s",
	      (Int)TNAM_OBJ(off), 0L);
  off1 = INT_INTOBJ(off);
  if (off1 < 0)
    {
       ErrorMayQuit("ADD_GF2VEC_GF2VEC_SHIFTED: <offset> must be a non-negative integer",
			   0,0);
    }
  len2a = INT_INTOBJ(len2);
  while (len2a < 0 && len2a <= LEN_GF2VEC(vec2)) 
    {
      len2 = ErrorReturnObj("ADD_GF2VEC_GF2VEC_SHIFTED: <len2> must be a non-negative integer\nand less than the actual length of the vector",
			   0,0,"you can replace <len2> via 'return <len2>;'");
      len2a = INT_INTOBJ(len2);
    }
  if (len2a + off1 > LEN_GF2VEC(vec1))
    ResizeGF2Vec(vec1, len2a+ off1);
  AddShiftedVecGF2VecGF2( vec1, vec2, len2a, off1);
  return (Obj) 0;
}

/****************************************************************************
**
*F  ProductCoeffsGF2Vec( <vec1>, <len1>, <vec2>, <len2> )
**
*/

Obj ProductCoeffsGF2Vec( Obj vec1, UInt len1, Obj vec2, UInt len2)
{
  Obj prod;
  UInt i,e;
  UInt *ptr;
  UInt block = 0;
  UInt len;
  if (len1 == 0 && len2 == 0)
    len = 0;
  else
    len = len1 + len2 -1;
  NEW_GF2VEC(prod, TYPE_LIST_GF2VEC, len);

  /* better to do the longer loop on the inside */
  if (len2 < len1)
    {
      UInt tmp;
      Obj tmpv;
      tmp = len1;
      len1 = len2;
      len2 = tmp;
      tmpv = vec1;
      vec1 = vec2;
      vec2 = tmpv;
    }

  ptr = BLOCKS_GF2VEC(vec1);
  e = BIPEB;
  for (i = 0; i < len1; i++)
    {
      if (e == BIPEB)
	{
	  block = *ptr++;
	  e = 0;
	}
      if (block & ((UInt)1 << e++))
	AddShiftedVecGF2VecGF2( prod, vec2, len2, i);
    }
  return prod;
}

/****************************************************************************
**
*F  FuncPROD_COEFFS_GF2VEC( <self>, <vec1>, <len1>, <vec2>, <len2> )
**
*/

Obj FuncPROD_COEFFS_GF2VEC( Obj self, Obj vec1, Obj len1, Obj vec2, Obj len2 )
{
  UInt len1a, len2a;
  Obj prod;
  UInt last;
  if (!ARE_INTOBJS(len1,len2))
    ErrorMayQuit("PROD_COEFFS_GF2VEC: vector lengths must be small integers, not a %s and a %s",
	      (Int)TNAM_OBJ(len1), (Int)TNAM_OBJ(len2));
  len2a = INT_INTOBJ(len2);
   if (len2a > LEN_GF2VEC(vec2)) 
       ErrorMayQuit("PROD_COEFFS_GF2VEC: <len2> must not be more than the actual\nlength of the vector",
		 0,0);
  len1a = INT_INTOBJ(len1);
  if (len1a > LEN_GF2VEC(vec1)) 
       ErrorMayQuit("PROD_COEFFS_GF2VEC: <len1> must be not more than the actual\nlength of the vector",
		 0,0);
  prod = ProductCoeffsGF2Vec( vec1, len1a, vec2, len2a );
  last = RightMostOneGF2Vec(prod);
  if (last < LEN_GF2VEC(prod))
    ResizeGF2Vec(prod, last);
  return prod;
}

/****************************************************************************
**
*F  ReduceCoeffsGF2Vec(<vec1>, <vec2>, <len2>, <quotient> )
**
*/

void ReduceCoeffsGF2Vec( Obj vec1, Obj vec2, UInt len2, Obj quotient )
{
  UInt len1 = LEN_GF2VEC(vec1);
  UInt i,j,e;
  UInt *ptr, *qptr = (UInt *)0;
  if (len2 > len1)
    return;
  i = len1 -1;
  e = (i % BIPEB);
  ptr = BLOCKS_GF2VEC(vec1) + (i/BIPEB);
  if (quotient != (Obj) 0)
    qptr = BLOCKS_GF2VEC(quotient);
  j = len1-len2+1;
  while (i+ 1 >= len2)
    {
      if (*ptr & ((UInt)1 << e))
	{
	  AddShiftedVecGF2VecGF2(vec1, vec2, len2, i - len2 + 1);
	  if (qptr)
	    qptr[(j-1)/BIPEB] |= MASK_POS_GF2VEC(j);
	}
      assert(!(*ptr & ((UInt)1<<e)));
      if (e == 0)
	{
	  e = BIPEB -1;
	  ptr--;
	}
      else
	e--;
      i--;
      j--;
    }
}

/****************************************************************************
**
*F  FuncREDUCE_COEFFS_GF2VEC( <self>, <vec1>, <len1>, <vec2>, <len2> )
**
*/
Obj FuncREDUCE_COEFFS_GF2VEC( Obj self, Obj vec1, Obj len1, Obj vec2, Obj len2)
{
  UInt last;
  Int len2a;
  if (!IS_INTOBJ(len1))
    ErrorMayQuit("REDUCE_COEFFS_GF2VEC: given length <len1> of left argt must be a small integer, not a %s",
	      (Int)TNAM_OBJ(len1),0L);
  if (INT_INTOBJ(len1) < 0 || INT_INTOBJ(len1) > LEN_GF2VEC(vec1))
    ErrorMayQuit("ReduceCoeffs: given length <len1> of left argt (%d)\nis longer than the argt (%d)",
	      INT_INTOBJ(len1), LEN_GF2VEC(vec1));
  if (!IS_INTOBJ(len2))
    ErrorMayQuit("REDUCE_COEFFS_GF2VEC: given length <len2> of right argt must be a small integer, not a %s",
	      (Int)TNAM_OBJ(len2),0L);
  len2a = INT_INTOBJ(len2);
  if ( len2a < 0 ||  len2a > LEN_GF2VEC(vec2))
    ErrorMayQuit("ReduceCoeffs: given length <len2> of right argt (%d)\nis longer than the argt (%d)",
		     len2a, LEN_GF2VEC(vec2));
  ResizeGF2Vec(vec1, INT_INTOBJ(len1));
  
  while ( 0 < len2a ) {
    if ( BLOCK_ELM_GF2VEC(vec2,len2a) == 0 )
      len2a = BIPEB*((len2a-1)/BIPEB);
    else if ( BLOCK_ELM_GF2VEC(vec2,len2a) & MASK_POS_GF2VEC(len2a) )
      break;
    else
      len2a--;
  }

  if (len2a == 0)
    {
      ErrorReturnVoid("ReduceCoeffs: second argument must not be zero", 0, 0,
		      "you may 'return;' to skip the reduction");
      return 0;
    }
  
  ReduceCoeffsGF2Vec( vec1, vec2, len2a, (Obj)0);
  last = RightMostOneGF2Vec(vec1);
  ResizeGF2Vec(vec1, last);
  return INTOBJ_INT(last);
}

/****************************************************************************
**
*F  FuncQUOTREM_COEFFS_GF2VEC( <self>, <vec1>, <len1>, <vec2>, <len2> )
**
*/
Obj FuncQUOTREM_COEFFS_GF2VEC( Obj self, Obj vec1, Obj len1, Obj vec2, Obj len2)
{
     Int len2a;
     Int len1a = INT_INTOBJ(len1);
     Obj quotv, remv, ret;
     if (!IS_INTOBJ(len1))
     ErrorMayQuit("QUOTREM_COEFFS_GF2VEC: given length <len1> of left argt must be a small integer, not a %s",
		  (Int)TNAM_OBJ(len1),0L);
     if (INT_INTOBJ(len1) < 0 || INT_INTOBJ(len1) > LEN_GF2VEC(vec1))
     ErrorMayQuit("QuotremCoeffs: given length <len1> of left argt (%d)\nis longer than the argt (%d)",
		  INT_INTOBJ(len1), LEN_GF2VEC(vec1));
     if (!IS_INTOBJ(len2))
     ErrorMayQuit("QUOTREM_COEFFS_GF2VEC: given length <len2> of right argt must be a small integer, not a %s",
		  (Int)TNAM_OBJ(len2),0L);
     len2a = INT_INTOBJ(len2);
     if ( len2a < 0 ||  len2a > LEN_GF2VEC(vec2))
     ErrorMayQuit("QuotremCoeffs: given length <len2> of right argt (%d)\nis longer than the argt (%d)",
		  len2a, LEN_GF2VEC(vec2));
     
     while ( 0 < len2a ) {
       if ( BLOCK_ELM_GF2VEC(vec2,len2a) == 0 )
	 len2a = BIPEB*((len2a-1)/BIPEB);
       else if ( BLOCK_ELM_GF2VEC(vec2,len2a) & MASK_POS_GF2VEC(len2a) )
	 break;
       else
	 len2a--;
     } 
     if (len2a == 0) {
       ErrorReturnVoid("QuotremCoeffs: second argument must not be zero", 0, 0,
		       "you may 'return;' to skip the reduction");
       return 0;
     }

     NEW_GF2VEC(remv, TYPE_LIST_GF2VEC, len1a);
     memcpy(BLOCKS_GF2VEC(remv), BLOCKS_GF2VEC(vec1),
	    ((len1a + BIPEB-1)/BIPEB)*sizeof(UInt));
     
     NEW_GF2VEC(quotv, TYPE_LIST_GF2VEC, len1a-len2a+1);
     ReduceCoeffsGF2Vec( remv, vec2, len2a, quotv);
     
     ret = NEW_PLIST(T_PLIST_TAB, 2);
     SET_LEN_PLIST(ret, 2);
     
     SET_ELM_PLIST(ret, 1, quotv);
     SET_ELM_PLIST(ret, 2, remv);

     CHANGED_BAG(ret);

return ret;
}


/****************************************************************************
**
*F  FuncSEMIECHELON_LIST_GF2VECS( <self>, <mat> )
**
**  Method for SemiEchelonMat for plain lists of GF2 vectors
**
** Method selection can guarantee us a plain list of characteristic 2 vectors 
*/

Obj FuncSEMIECHELON_LIST_GF2VECS( Obj self, Obj mat )
{
  UInt i,len;
  UInt width;
  Obj row;
  /* check argts */
  len = LEN_PLIST(mat);
  if (!len)
    return TRY_NEXT_METHOD;
  row = ELM_PLIST(mat,1);
  if (!IS_MUTABLE_OBJ(row) || !IS_GF2VEC_REP(row))
    return TRY_NEXT_METHOD;
  width = LEN_GF2VEC(row);
  if (width == 0)
    return TRY_NEXT_METHOD;
  for (i = 2; i <= len; i++)
    {
      row = ELM_PLIST(mat, i);
      if (!IS_MUTABLE_OBJ(row) || !IS_GF2VEC_REP(row) ||
	  LEN_GF2VEC(row)!= width)
	{
	  return TRY_NEXT_METHOD;
	}
    }
  return SemiEchelonListGF2Vecs( mat, 0);
}

/****************************************************************************
**
*F  FuncSEMIECHELON_LIST_GF2VECS_TRANSFORMATIONS( <self>, <mat> )
**
**  Method for SemiEchelonMatTransformations for plain lists of GF2 vectors
**
** Method selection can guarantee us a plain list of characteristic 2 vectors 
*/

Obj FuncSEMIECHELON_LIST_GF2VECS_TRANSFORMATIONS( Obj self, Obj mat )
{
  UInt i,len;
  UInt width;
  Obj row;
  /* check argts */
  len = LEN_PLIST(mat);
  if (!len)
    return TRY_NEXT_METHOD;
  row = ELM_PLIST(mat,1);
  if (!IS_MUTABLE_OBJ(row) || !IS_GF2VEC_REP(row))
    return TRY_NEXT_METHOD;
  width = LEN_GF2VEC(row);
  if (width == 0)
    return TRY_NEXT_METHOD;
  for (i = 2; i <= len; i++)
    {
      row = ELM_PLIST(mat, i);
      if (!IS_MUTABLE_OBJ(row) || !IS_GF2VEC_REP(row) ||
	  LEN_GF2VEC(row)!= width)
	{
	  return TRY_NEXT_METHOD;
	}
    }
  return SemiEchelonListGF2Vecs( mat, 1);
}

/****************************************************************************
**
*F  FuncTRIANGULIZE_LIST_GF2VECS( <self>, <mat> )
**
*/

Obj FuncTRIANGULIZE_LIST_GF2VECS( Obj self, Obj mat)
{
  UInt i,len;
  UInt width;
  Obj row;
  /* check argts */
  len = LEN_PLIST(mat);
  if (!len)
    return TRY_NEXT_METHOD;
  row = ELM_PLIST(mat,1);
  if (!IS_MUTABLE_OBJ(row) || !IS_GF2VEC_REP(row))
    return TRY_NEXT_METHOD;
  width = LEN_GF2VEC(row);
  if (width == 0)
    return TRY_NEXT_METHOD;
  for (i = 2; i <= len; i++)
    {
      row = ELM_PLIST(mat, i);
      if (!IS_MUTABLE_OBJ(row) || !IS_GF2VEC_REP(row) ||
	  LEN_GF2VEC(row)!= width)
	{
	  return TRY_NEXT_METHOD;
	}
    }
  TriangulizeListGF2Vecs( mat, 1 );
  return (Obj) 0;
}

/****************************************************************************
**
*F  FuncRANK_LIST_GF2VECS( <self>, <mat> )
**
*/

Obj FuncRANK_LIST_GF2VECS( Obj self, Obj mat)
{
  UInt i,len;
  UInt width;
  Obj row;
  /* check argts */
  len = LEN_PLIST(mat);
  if (!len)
    return TRY_NEXT_METHOD;
  row = ELM_PLIST(mat,1);
  if (!IS_MUTABLE_OBJ(row) || !IS_GF2VEC_REP(row))
    return TRY_NEXT_METHOD;
  width = LEN_GF2VEC(row);
  if (width == 0)
    return TRY_NEXT_METHOD;
  for (i = 2; i <= len; i++)
    {
      row = ELM_PLIST(mat, i);
      if (!IS_MUTABLE_OBJ(row) || !IS_GF2VEC_REP(row) ||
	  LEN_GF2VEC(row)!= width)
	{
	  return TRY_NEXT_METHOD;
	}
    }
  return INTOBJ_INT(TriangulizeListGF2Vecs( mat , 0));
}

/****************************************************************************
**
*F  FuncDETERMINANT_LIST_GF2VECS( <self>, <mat> )
**
*/

Obj FuncDETERMINANT_LIST_GF2VECS( Obj self, Obj mat)
{
  UInt i,len;
  UInt width;
  Obj row;
  /* check argts */
  len = LEN_PLIST(mat);
  if (!len)
    return TRY_NEXT_METHOD;
  row = ELM_PLIST(mat,1);
  if (!IS_MUTABLE_OBJ(row) || !IS_GF2VEC_REP(row))
    return TRY_NEXT_METHOD;
  width = LEN_GF2VEC(row);
  if (width == 0)
    return TRY_NEXT_METHOD;
  for (i = 2; i <= len; i++)
    {
      row = ELM_PLIST(mat, i);
      if (!IS_MUTABLE_OBJ(row) || !IS_GF2VEC_REP(row) ||
	  LEN_GF2VEC(row)!= width)
	{
	  return TRY_NEXT_METHOD;
	}
    }
  return (len == TriangulizeListGF2Vecs( mat , 0)) ? GF2One : GF2Zero;
}

/****************************************************************************
**
*F  FuncKRONECKERPRODUCT_GF2MAT_GF2MAT( <self>, <matl>, <matr>)
**
*/

Obj FuncKRONECKERPRODUCT_GF2MAT_GF2MAT( Obj self, Obj matl, Obj matr)
{
  UInt nrowl, nrowr, nrowp, ncoll, ncolr, ncolp, ncol,
    i, j, k, l, mutable;
  Obj mat, type, row, shift[BIPEB];
  UInt *data;
  const UInt *datar;

  nrowl = LEN_GF2MAT(matl);
  nrowr = LEN_GF2MAT(matr);
  nrowp = nrowl*nrowr;
  ncoll = LEN_GF2VEC(ELM_GF2MAT(matl,1));
  ncolr = LEN_GF2VEC(ELM_GF2MAT(matr,1));
  ncolp = ncoll*ncolr;

  mutable = IS_MUTABLE_OBJ(matl) || IS_MUTABLE_OBJ(matr);

  /* create a matrix */
  mat = NewBag(T_POSOBJ, SIZE_PLEN_GF2MAT(nrowp));
  SET_LEN_GF2MAT(mat,nrowp);
  if (mutable) {
      SET_TYPE_POSOBJ(mat, TYPE_LIST_GF2MAT);
      type = TYPE_LIST_GF2VEC_LOCKED;
  } else {
      SET_TYPE_POSOBJ(mat, TYPE_LIST_GF2MAT_IMM);
      type = TYPE_LIST_GF2VEC_IMM_LOCKED;
  }

  /* allocate 0 matrix */

  for (i = 1; i <= nrowp; i++) {
    NEW_GF2VEC(row, type, ncolp);
    SET_ELM_GF2MAT(mat,i,row);
    CHANGED_BAG(mat);
  }

  /* allocate data for shifts of rows of matr */
  for (i = 0; i < BIPEB; i++) {
    shift[i] = NewBag(T_DATOBJ, SIZE_PLEN_GF2VEC(ncolr+2*BIPEB));
  }

  /* fill in matrix */
  for (j = 1; j <= nrowr; j++) {
    /* create shifts of rows of matr */
    data = (UInt *) ADDR_OBJ(shift[0]);
    datar = BLOCKS_GF2VEC(ELM_GF2MAT(matr,j));
    for (k = 0; k < (ncolr+BIPEB-1)/BIPEB; k++)
      data[k] = datar[k];
    data[k] = 0;
    
    for (i = 1; i < BIPEB; i++) { /* now shifts in [1..BIPEB-1] */
      data = (UInt *) ADDR_OBJ(shift[i]);
      datar = BLOCKS_GF2VEC(ELM_GF2MAT(matr,j));
      data[0] = datar[0] << i;
      for (k = 1; k < (ncolr+BIPEB-1)/BIPEB; k++)
	data[k] = (datar[k] << i) | (datar[k-1] >> (BIPEB-i));
      data[k] = datar[k-1] >> (BIPEB-i);
    }
    for (i = 1; i <= nrowl; i++) {
      data = BLOCKS_GF2VEC(ELM_GF2MAT(mat,(i-1)*nrowr+j));
      ncol = 0;
      for (k = 1; k <= ncoll; k++) {
	l = 0;
	if (BLOCK_ELM_GF2VEC(ELM_GF2MAT(matl,i),k) & MASK_POS_GF2VEC(k)) {
	  /* append shift[ncol%BIPEB] to data */
	  datar = (const UInt *) CONST_ADDR_OBJ(shift[ncol%BIPEB]);
	  if (ncol % BIPEB) {
	    data[-1] ^= *datar++;
	    l = BIPEB - ncol%BIPEB;
	  }
	  for (; l < ncolr; l += BIPEB)
	    *data++ = *datar++;
	} else {
	  if (ncol % BIPEB)
	    l = BIPEB - ncol%BIPEB;
	  data += (ncolr+BIPEB-1-l)/BIPEB;
	}
	ncol += ncolr;
      }
    }
  }

  return mat;
}


/****************************************************************************
**
*F  FuncMAT_ELM_GF2MAT( <self>, <mat>, <row>, <col> )
**
*/
Obj FuncMAT_ELM_GF2MAT( Obj self, Obj mat, Obj row, Obj col )
{
    if (!IS_POS_INTOBJ(row)) {
        ErrorMayQuit("row index must be a small positive integer, not a %s",
                     (Int)TNAM_OBJ(row), 0L);
    }
    if (!IS_POS_INTOBJ(col)) {
        ErrorMayQuit("column index must be a small positive integer, not a %s",
                     (Int)TNAM_OBJ(col), 0L);
    }

    UInt r = INT_INTOBJ(row);
    if (LEN_GF2MAT(mat) < r) {
        ErrorMayQuit("row index %d exceeds %d, the number of rows", r, LEN_GF2MAT(mat));
    }

    Obj vec = ELM_GF2MAT(mat, r);

    UInt c = INT_INTOBJ(col);
    if (LEN_GF2VEC(vec) < c) {
        ErrorMayQuit("column index %d exceeds %d, the number of columns", c, LEN_GF2VEC(vec));
    }

    return ELM_GF2VEC(vec, c);
}


/****************************************************************************
**
*F  FuncSET_MAT_ELM_GF2MAT( <self>, <mat>, <row>, <col>, <elm> )
**
*/
Obj FuncSET_MAT_ELM_GF2MAT( Obj self, Obj mat, Obj row, Obj col, Obj elm )
{
    if (!IS_POS_INTOBJ(row)) {
        ErrorMayQuit("row index must be a small positive integer, not a %s",
                     (Int)TNAM_OBJ(row), 0L);
    }
    if (!IS_POS_INTOBJ(col)) {
        ErrorMayQuit("column index must be a small positive integer, not a %s",
                     (Int)TNAM_OBJ(col), 0L);
    }

    UInt r = INT_INTOBJ(row);
    if (LEN_GF2MAT(mat) < r) {
        ErrorMayQuit("row index %d exceeds %d, the number of rows", r, LEN_GF2MAT(mat));
    }

    Obj vec = ELM_GF2MAT(mat, r);
    if ( ! IS_MUTABLE_OBJ(vec) ) {
        ErrorMayQuit("row %d is immutable", r, 0);
    }

    UInt c = INT_INTOBJ(col);
    if (LEN_GF2VEC(vec) < c) {
        ErrorMayQuit("column index %d exceeds %d, the number of columns", c, LEN_GF2VEC(vec));
    }

    if ( EQ(GF2One, elm) ) {
        BLOCK_ELM_GF2VEC(vec,c) |= MASK_POS_GF2VEC(c);
    }
    else if ( EQ(GF2Zero, elm) ) {
        BLOCK_ELM_GF2VEC(vec,c) &= ~MASK_POS_GF2VEC(c);
    }
    else {
        ErrorMayQuit("SET_MAT_ELM_GF2MAT: assigned element must be a GF(2) element, not a %s",
                     (Int)TNAM_OBJ(elm), 0L);
    }

    return 0;
}


/****************************************************************************
**
*F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * *
*/


/****************************************************************************
**
*V  GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
*/
static StructGVarFunc GVarFuncs [] = {

    GVAR_FUNC(CONV_GF2VEC, 1, "list"),
    GVAR_FUNC(COPY_GF2VEC, 1, "list"),
    GVAR_FUNC(PLAIN_GF2VEC, 1, "gf2vec"),
    GVAR_FUNC(PLAIN_GF2MAT, 1, "gf2mat"),
    GVAR_FUNC(EQ_GF2VEC_GF2VEC, 2, "gf2vec, gf2vec"),
    GVAR_FUNC(LT_GF2VEC_GF2VEC, 2, "gf2vec, gf2vec"),
    GVAR_FUNC(EQ_GF2MAT_GF2MAT, 2, "gf2mat, gf2mat"),
    GVAR_FUNC(LT_GF2MAT_GF2MAT, 2, "gf2mat, gf2mat"),
    GVAR_FUNC(LEN_GF2VEC, 1, "gf2vec"),
    GVAR_FUNC(ELM0_GF2VEC, 2, "gf2vec, pos"),
    GVAR_FUNC(ELM_GF2VEC, 2, "gf2vec, pos"),
    GVAR_FUNC(ELMS_GF2VEC, 2, "gf2vec, poss"),
    GVAR_FUNC(ASS_GF2VEC, 3, "gf2vec, pos, elm"),
    GVAR_FUNC(ELM_GF2MAT, 2, "gf2mat, pos"),
    GVAR_FUNC(ASS_GF2MAT, 3, "gf2mat, pos, elm"),
    GVAR_FUNC(UNB_GF2VEC, 2, "gf2vec, pos"),
    GVAR_FUNC(UNB_GF2MAT, 2, "gf2mat, pos"),
    GVAR_FUNC(ZERO_GF2VEC, 1, "gf2vec"),
    GVAR_FUNC(ZERO_GF2VEC_2, 1, "len"),
    GVAR_FUNC(INV_GF2MAT_MUTABLE, 1, "gf2mat"),
    GVAR_FUNC(INV_GF2MAT_SAME_MUTABILITY, 1, "gf2mat"),
    GVAR_FUNC(INV_GF2MAT_IMMUTABLE, 1, "gf2mat"),
    GVAR_FUNC(INV_PLIST_GF2VECS_DESTRUCTIVE, 1, "list"),
    GVAR_FUNC(SUM_GF2VEC_GF2VEC, 2, "gf2vec, gf2vec"),
    GVAR_FUNC(PROD_GF2VEC_GF2VEC, 2, "gf2vec, gf2vec"),
    GVAR_FUNC(PROD_GF2VEC_GF2MAT, 2, "gf2vec, gf2mat"),
    GVAR_FUNC(PROD_GF2MAT_GF2VEC, 2, "gf2mat, gf2vec"),
    GVAR_FUNC(PROD_GF2MAT_GF2MAT, 2, "gf2matl, gf2matr"),
    GVAR_FUNC(PROD_GF2MAT_GF2MAT_SIMPLE, 2, "gf2matl, gf2matr"),
    GVAR_FUNC(PROD_GF2MAT_GF2MAT_ADVANCED, 4, "gf2matl, gf2matr, greaselevel, blocklevel"),
    GVAR_FUNC(ADDCOEFFS_GF2VEC_GF2VEC_MULT, 3, "gf2vec, gf2vec, mul"),
    GVAR_FUNC(ADDCOEFFS_GF2VEC_GF2VEC, 2, "gf2vec, gf2vec"),
    GVAR_FUNC(SHRINKCOEFFS_GF2VEC, 1, "gf2vec"),
    GVAR_FUNC(POSITION_NONZERO_GF2VEC, 2, "gf2vec, zero"),
    GVAR_FUNC(POSITION_NONZERO_GF2VEC3, 3, "gf2vec, zero, from"),
    GVAR_FUNC(MULT_ROW_VECTOR_GF2VECS_2, 2, "gf2vecl, mul"),
    GVAR_FUNC(APPEND_GF2VEC, 2, "gf2vecl, gf2vecr"),
    GVAR_FUNC(SHALLOWCOPY_GF2VEC, 1, "gf2vec"),
    GVAR_FUNC(NUMBER_GF2VEC, 1, "gf2vec"),
    GVAR_FUNC(TRANSPOSED_GF2MAT, 1, "gf2mat"),
    GVAR_FUNC(DIST_GF2VEC_GF2VEC, 2, "gf2vec, gf2vec"),
    GVAR_FUNC(DIST_VEC_CLOS_VEC, 3, "list, gf2vec, list"),
    GVAR_FUNC(SUM_GF2MAT_GF2MAT, 2, "matl, matr"),
    GVAR_FUNC(A_CLOS_VEC, 4, "list, gf2vec, int, int"),
    GVAR_FUNC(A_CLOS_VEC_COORDS, 4, "list, gf2vec, int, int"),
    GVAR_FUNC(COSET_LEADERS_INNER_GF2, 4, "veclis, weight, tofind, leaders"),
    GVAR_FUNC(CONV_GF2MAT, 1, "list"),
    GVAR_FUNC(PROD_GF2VEC_ANYMAT, 2, "vec, mat"),
    GVAR_FUNC(RIGHTMOST_NONZERO_GF2VEC, 1, "vec"),
    GVAR_FUNC(RESIZE_GF2VEC, 2, "vec, newlen"),
    GVAR_FUNC(SHIFT_LEFT_GF2VEC, 2, "vec, amount"),
    GVAR_FUNC(SHIFT_RIGHT_GF2VEC, 3, "vec, amount, zero"),
    GVAR_FUNC(ADD_GF2VEC_GF2VEC_SHIFTED, 4, "vec1, vec2,len2, off"),
    GVAR_FUNC(PROD_COEFFS_GF2VEC, 4, "vec1, len1, vec2,len2"),
    GVAR_FUNC(REDUCE_COEFFS_GF2VEC, 4, "vec1, len1, vec2,len2"),
    GVAR_FUNC(QUOTREM_COEFFS_GF2VEC, 4, "vec1, len1, vec2,len2"),
    GVAR_FUNC(SEMIECHELON_LIST_GF2VECS, 1, "mat"),
    GVAR_FUNC(SEMIECHELON_LIST_GF2VECS_TRANSFORMATIONS, 1, "mat"),
    GVAR_FUNC(TRIANGULIZE_LIST_GF2VECS, 1, "mat"),
    GVAR_FUNC(DETERMINANT_LIST_GF2VECS, 1, "mat"),
    GVAR_FUNC(RANK_LIST_GF2VECS, 1, "mat"),
    GVAR_FUNC(KRONECKERPRODUCT_GF2MAT_GF2MAT, 2, "mat, mat"),
    GVAR_FUNC(COPY_SECTION_GF2VECS, 5, "src, dest, from, to, howmany"),
    GVAR_FUNC(MAT_ELM_GF2MAT, 3, "mat, row, col"),
    GVAR_FUNC(SET_MAT_ELM_GF2MAT, 4, "mat, row, col, elm"),
    { 0, 0, 0, 0, 0 }

};


/****************************************************************************
**
*F  InitKernel( <module> )  . . . . . . . . initialise kernel data structures
*/
static Int InitKernel (
    StructInitInfo *    module )
{
  RNheads = 0;
  RNvectors = 0;
  RNcoeffs = 0;
  RNrelns = 0;

    /* import type functions                                               */
    ImportGVarFromLibrary( "TYPE_LIST_GF2VEC",     &TYPE_LIST_GF2VEC     );
    ImportGVarFromLibrary( "TYPE_LIST_GF2VEC_IMM", &TYPE_LIST_GF2VEC_IMM );
    ImportGVarFromLibrary( "TYPE_LIST_GF2VEC_IMM_LOCKED", &TYPE_LIST_GF2VEC_IMM_LOCKED );
    ImportGVarFromLibrary( "TYPE_LIST_GF2VEC_LOCKED", &TYPE_LIST_GF2VEC_LOCKED );
    ImportFuncFromLibrary( "IsGF2VectorRep",       &IsGF2VectorRep       );
    ImportGVarFromLibrary( "TYPE_LIST_GF2MAT",     &TYPE_LIST_GF2MAT     );
    ImportGVarFromLibrary( "TYPE_LIST_GF2MAT_IMM", &TYPE_LIST_GF2MAT_IMM );

    /* initialize one and zero of GF2                                      */
    ImportGVarFromLibrary( "GF2One",  &GF2One  );
    ImportGVarFromLibrary( "GF2Zero", &GF2Zero );

    /* init filters and functions                                          */
    InitHdlrFuncsFromTable( GVarFuncs );

    InitFopyGVar("IsLockedRepresentationVector", &IsLockedRepresentationVector);

    /* return success                                                      */
    return 0;
}


/****************************************************************************
**
*F  InitLibrary( <module> ) . . . . . . .  initialise library data structures
*/
static Int InitLibrary (
    StructInitInfo *    module )
{
    /* init filters and functions                                          */
    InitGVarFuncsFromTable( GVarFuncs );

    /* return success                                                      */
    return 0;
}


/****************************************************************************
**
*F  InitInfoGF2Vec()  . . . . . . . . . . . . . . . . table of init functions
*/
static StructInitInfo module = {
    // init struct using C99 designated initializers; for a full list of
    // fields, please refer to the definition of StructInitInfo
    .type = MODULE_BUILTIN,
    .name = "vecgf2",
    .initKernel = InitKernel,
    .initLibrary = InitLibrary,
};

StructInitInfo * InitInfoGF2Vec ( void )
{
    return &module;
}
