[BACK]Return to array.c CVS log [TXT][DIR] Up to [local] / OpenXM_contrib2 / asir2000 / builtin

File: [local] / OpenXM_contrib2 / asir2000 / builtin / array.c (download)

Revision 1.4, Mon May 29 08:54:44 2000 UTC (23 years, 11 months ago) by noro
Branch: MAIN
Changes since 1.3: +200 -19 lines

1. F4

	array.c, gr.c (still experimental)

2. Weyl algebra

	dist.c, distm.c : product of monomials (weyl_mul*)
	Q.c : coefficients of the expansion of D^k*x^l (mkwc, mkwcm)

    Note that the current implementation and specification are too ad hoc.

	If ctrl("do_weyl",1) is executed, then all monomial operations are
	done in Weyl algebra. If the length of the exponent of a monomial
	is n=2m, then it is regarded as an element of Q<x1,...,xm,Dx1,...,Dxm>.
	If the length is n=2m+1, then it is a regarded as an element of
	Q[h]<x1,...,xm,Dx1,...,Dxm>, where h is the homogenization variable.
	The order specification is the same as in the commutative case, so
	one should use matrix order to realize natural orderings in Weyl
	algebra. Negative waits have not yet been supported.

/* $OpenXM: OpenXM_contrib2/asir2000/builtin/array.c,v 1.4 2000/05/29 08:54:44 noro Exp $ */
#include "ca.h"
#include "base.h"
#include "parse.h"
#include "inline.h"

#if 0
#undef DMAR
#define DMAR(a1,a2,a3,d,r) (r)=dmar(a1,a2,a3,d);
#endif

extern int Print; /* XXX */

void inner_product_mat_int_mod(Q **,int **,int,int,int,Q *);
void solve_by_lu_mod(int **,int,int,int **,int);
void solve_by_lu_gfmmat(GFMMAT,unsigned int,unsigned int *,unsigned int *);
int lu_gfmmat(GFMMAT,unsigned int,int *);
void mat_to_gfmmat(MAT,unsigned int,GFMMAT *);

int generic_gauss_elim_mod(int **,int,int,int,int *);
int generic_gauss_elim(MAT ,MAT *,Q *,int **,int **);

int gauss_elim_mod(int **,int,int,int);
int gauss_elim_mod1(int **,int,int,int);
int gauss_elim_geninv_mod(unsigned int **,int,int,int);
int gauss_elim_geninv_mod_swap(unsigned int **,int,int,unsigned int,unsigned int ***,int **);
void Pnewvect(), Pnewmat(), Psepvect(), Psize(), Pdet(), Pleqm(), Pleqm1(), Pgeninvm();

void Pgeneric_gauss_elim_mod();

void Pmat_to_gfmmat(),Plu_gfmmat(),Psolve_by_lu_gfmmat();
void Pgeninvm_swap(), Premainder(), Psremainder(), Pvtol();
void sepvect();
void Pmulmat_gf2n();
void Pbconvmat_gf2n();
void Pmul_vect_mat_gf2n();
void PNBmul_gf2n();
void Pmul_mat_vect_int();
void Psepmat_destructive();
void Px962_irredpoly_up2();
void Pirredpoly_up2();
void Pnbpoly_up2();
void Pqsort();

struct ftab array_tab[] = {
	{"solve_by_lu_gfmmat",Psolve_by_lu_gfmmat,4},
	{"lu_gfmmat",Plu_gfmmat,2},
	{"mat_to_gfmmat",Pmat_to_gfmmat,2},
	{"generic_gauss_elim_mod",Pgeneric_gauss_elim_mod,2},
	{"newvect",Pnewvect,-2},
	{"newmat",Pnewmat,-3},
	{"sepmat_destructive",Psepmat_destructive,2},
	{"sepvect",Psepvect,2},
	{"qsort",Pqsort,-2},
	{"vtol",Pvtol,1},
	{"size",Psize,1},
	{"det",Pdet,-2},
	{"leqm",Pleqm,2},
	{"leqm1",Pleqm1,2},
	{"geninvm",Pgeninvm,2},
	{"geninvm_swap",Pgeninvm_swap,2},
	{"remainder",Premainder,2},
	{"sremainder",Psremainder,2},
	{"mulmat_gf2n",Pmulmat_gf2n,1},
	{"bconvmat_gf2n",Pbconvmat_gf2n,-4},
	{"mul_vect_mat_gf2n",Pmul_vect_mat_gf2n,2},
	{"mul_mat_vect_int",Pmul_mat_vect_int,2},
	{"nbmul_gf2n",PNBmul_gf2n,3},
	{"x962_irredpoly_up2",Px962_irredpoly_up2,2},
	{"irredpoly_up2",Pirredpoly_up2,2},
	{"nbpoly_up2",Pnbpoly_up2,2},
	{0,0,0},
};

int comp_obj(a,b)
Obj *a,*b;
{
	return arf_comp(CO,*a,*b);
}

static FUNC generic_comp_obj_func;
static NODE generic_comp_obj_arg;

int generic_comp_obj(a,b)
Obj *a,*b;
{
	Q r;
	
	BDY(generic_comp_obj_arg)=(pointer)(*a);
	BDY(NEXT(generic_comp_obj_arg))=(pointer)(*b);
	r = (Q)bevalf(generic_comp_obj_func,generic_comp_obj_arg);
	if ( !r )
		return 0;
	else
		return SGN(r)>0?1:-1;
}


void Pqsort(arg,rp)
NODE arg;
VECT *rp;
{
	VECT vect;
	char buf[BUFSIZ];
	char *fname;
	NODE n;
	P p;
	V v;

	asir_assert(ARG0(arg),O_VECT,"qsort");
	vect = (VECT)ARG0(arg);
	if ( argc(arg) == 1 )
		qsort(BDY(vect),vect->len,sizeof(Obj),(int (*)(const void *,const void *))comp_obj);
	else {
		p = (P)ARG1(arg);
		if ( !p || OID(p)!=2 )
			error("qsort : invalid argument");
		v = VR(p);
		if ( (int)v->attr != V_SR )
			error("qsort : no such function");
		generic_comp_obj_func = (FUNC)v->priv;
		MKNODE(n,0,0); MKNODE(generic_comp_obj_arg,0,n);	
		qsort(BDY(vect),vect->len,sizeof(Obj),(int (*)(const void *,const void *))generic_comp_obj);
	}
	*rp = vect;
}

void PNBmul_gf2n(arg,rp)
NODE arg;
GF2N *rp;
{
	GF2N a,b;
	GF2MAT mat;
	int n,w;
	unsigned int *ab,*bb;
	UP2 r;

	a = (GF2N)ARG0(arg);
	b = (GF2N)ARG1(arg);
	mat = (GF2MAT)ARG2(arg);
	if ( !a || !b )
		*rp = 0;
	else {
		n = mat->row;
		w = (n+BSH-1)/BSH;

		ab = (unsigned int *)ALLOCA(w*sizeof(unsigned int));
		bzero((char *)ab,w*sizeof(unsigned int));
		bcopy(a->body->b,ab,(a->body->w)*sizeof(unsigned int));

		bb = (unsigned int *)ALLOCA(w*sizeof(unsigned int));
		bzero((char *)bb,w*sizeof(unsigned int));
		bcopy(b->body->b,bb,(b->body->w)*sizeof(unsigned int));

		NEWUP2(r,w);
		bzero((char *)r->b,w*sizeof(unsigned int));
		mul_nb(mat,ab,bb,r->b);
		r->w = w;
		_adjup2(r);
		if ( !r->w )
			*rp = 0;
		else
			MKGF2N(r,*rp);
	}
}

void Pmul_vect_mat_gf2n(arg,rp)
NODE arg;
GF2N *rp;
{
	GF2N a;
	GF2MAT mat;
	int n,w;
	unsigned int *b;
	UP2 r;

	a = (GF2N)ARG0(arg);
	mat = (GF2MAT)ARG1(arg);
	if ( !a )
		*rp = 0;
	else {
		n = mat->row;
		w = (n+BSH-1)/BSH;
		b = (unsigned int *)ALLOCA(w*sizeof(unsigned int));
		bzero((char *)b,w*sizeof(unsigned int));
		bcopy(a->body->b,b,(a->body->w)*sizeof(unsigned int));
		NEWUP2(r,w);
		bzero((char *)r->b,w*sizeof(unsigned int));
		mulgf2vectmat(mat->row,b,mat->body,r->b);
		r->w = w;
		_adjup2(r);
		if ( !r->w )
			*rp = 0;
		else {
			MKGF2N(r,*rp);
		}
	}
}

void Pbconvmat_gf2n(arg,rp)
NODE arg;
LIST *rp;
{
	P p0,p1;
	int to;
	GF2MAT p01,p10;
	GF2N root;
	NODE n0,n1;

	p0 = (P)ARG0(arg);
	p1 = (P)ARG1(arg);
	to = ARG2(arg)?1:0;
	if ( argc(arg) == 4 ) {
		root = (GF2N)ARG3(arg);
		compute_change_of_basis_matrix_with_root(p0,p1,to,root,&p01,&p10);
	} else
		compute_change_of_basis_matrix(p0,p1,to,&p01,&p10);
	MKNODE(n1,p10,0); MKNODE(n0,p01,n1);	
	MKLIST(*rp,n0);
}

void Pmulmat_gf2n(arg,rp)
NODE arg;
GF2MAT *rp;
{
	GF2MAT m;

	if ( !compute_multiplication_matrix((P)ARG0(arg),&m) )
		error("mulmat_gf2n : input is not a normal polynomial");
	*rp = m;
}

void Psepmat_destructive(arg,rp)
NODE arg;
LIST *rp;
{
	MAT mat,mat1;
	int i,j,row,col;
	Q **a,**a1;
	Q ent;
	N nm,mod,rem,quo;
	int sgn;
	NODE n0,n1;

	mat = (MAT)ARG0(arg); mod = NM((Q)ARG1(arg));
	row = mat->row; col = mat->col;
	MKMAT(mat1,row,col);
	a = (Q **)mat->body; a1 = (Q **)mat1->body;
	for ( i = 0; i < row; i++ )
		for ( j = 0; j < col; j++ ) {
			ent = a[i][j];
			if ( !ent )
				continue;
			nm = NM(ent);
			sgn = SGN(ent);
			divn(nm,mod,&quo,&rem);
/*			if ( quo != nm && rem != nm ) */
/*				GC_free(nm); */
/*			GC_free(ent); */
			NTOQ(rem,sgn,a[i][j]); NTOQ(quo,sgn,a1[i][j]);	
		}
	MKNODE(n1,mat1,0); MKNODE(n0,mat,n1);
	MKLIST(*rp,n0);
}

void Psepvect(arg,rp)
NODE arg;
VECT *rp;
{
	sepvect((VECT)ARG0(arg),QTOS((Q)ARG1(arg)),rp);
}

void sepvect(v,d,rp)
VECT v;
int d;
VECT *rp;
{
	int i,j,k,n,q,q1,r;
	pointer *pv,*pw,*pu;
	VECT w,u;

	n = v->len;
	if ( d > n )
		d = n;
	q = n/d; r = n%d; q1 = q+1;
	MKVECT(w,d); *rp = w;
	pv = BDY(v); pw = BDY(w); k = 0;
	for ( i = 0; i < r; i++ ) {
		MKVECT(u,q1); pw[i] = (pointer)u;
		for ( pu = BDY(u), j = 0; j < q1; j++, k++ )
			pu[j] = pv[k];
	}
	for ( ; i < d; i++ ) {
		MKVECT(u,q); pw[i] = (pointer)u;
		for ( pu = BDY(u), j = 0; j < q; j++, k++ )
			pu[j] = pv[k];
	}
}

void Pnewvect(arg,rp)
NODE arg;
VECT *rp;
{
	int len,i,r;
	VECT vect;
	pointer *vb;
	LIST list;
	NODE tn;

	asir_assert(ARG0(arg),O_N,"newvect");
	len = QTOS((Q)ARG0(arg)); 
	if ( len <= 0 )
		error("newvect : invalid size");
	MKVECT(vect,len);
	if ( argc(arg) == 2 ) {
		list = (LIST)ARG1(arg);
		asir_assert(list,O_LIST,"newvect");
		for ( r = 0, tn = BDY(list); tn; r++, tn = NEXT(tn) );
		if ( r > len ) {
			*rp = vect;
			return;
		}
		for ( i = 0, tn = BDY(list), vb = BDY(vect); tn; i++, tn = NEXT(tn) )
			vb[i] = (pointer)BDY(tn);
	}
	*rp = vect;
}

void Pnewmat(arg,rp)
NODE arg;
MAT *rp;
{
	int row,col;
	int i,j,r,c;
	NODE tn,sn;
	MAT m;
	pointer **mb;
	LIST list;

	asir_assert(ARG0(arg),O_N,"newmat");
	asir_assert(ARG1(arg),O_N,"newmat");
	row = QTOS((Q)ARG0(arg)); col = QTOS((Q)ARG1(arg));
	if ( row <= 0 || col <= 0 )
		error("newmat : invalid size");
	MKMAT(m,row,col);
	if ( argc(arg) == 3 ) {
		list = (LIST)ARG2(arg);
		asir_assert(list,O_LIST,"newmat");
		for ( r = 0, c = 0, tn = BDY(list); tn; r++, tn = NEXT(tn) ) {
			for ( j = 0, sn = BDY((LIST)BDY(tn)); sn; j++, sn = NEXT(sn) );
			c = MAX(c,j);
		}
		if ( (r > row) || (c > col) ) {
			*rp = m;
			return;
		}
		for ( i = 0, tn = BDY(list), mb = BDY(m); tn; i++, tn = NEXT(tn) ) {
			asir_assert(BDY(tn),O_LIST,"newmat");
			for ( j = 0, sn = BDY((LIST)BDY(tn)); sn; j++, sn = NEXT(sn) )
				mb[i][j] = (pointer)BDY(sn);
		}
	}
	*rp = m;
}

void Pvtol(arg,rp)
NODE arg;
LIST *rp;
{
	NODE n,n1;
	VECT v;
	pointer *a;
	int len,i;

	asir_assert(ARG0(arg),O_VECT,"vtol");
	v = (VECT)ARG0(arg); len = v->len; a = BDY(v);
	for ( i = len - 1, n = 0; i >= 0; i-- ) {
		MKNODE(n1,a[i],n); n = n1;
	}
	MKLIST(*rp,n);
}

void Premainder(arg,rp)
NODE arg;
Obj *rp;
{
	Obj a;
	VECT v,w;
	MAT m,l;
	pointer *vb,*wb;
	pointer **mb,**lb;
	int id,i,j,n,row,col,t,smd,sgn;
	Q md,q;

	a = (Obj)ARG0(arg); md = (Q)ARG1(arg);
	if ( !a )
		*rp = 0;
	else {
		id = OID(a);
		switch ( id ) {
			case O_N:
			case O_P:
				cmp(md,(P)a,(P *)rp); break;
			case O_VECT:
				smd = QTOS(md);
				v = (VECT)a; n = v->len; vb = v->body;
				MKVECT(w,n); wb = w->body;
				for ( i = 0; i < n; i++ ) {
					if ( q = (Q)vb[i] ) {
						sgn = SGN(q); t = rem(NM(q),smd);
						STOQ(t,q);
						if ( q )
							SGN(q) = sgn;
					}
					wb[i] = (pointer)q;
				}
				*rp = (Obj)w;
				break;
			case O_MAT:
				m = (MAT)a; row = m->row; col = m->col; mb = m->body;
				MKMAT(l,row,col); lb = l->body;
				for ( i = 0; i < row; i++ )
					for ( j = 0, vb = mb[i], wb = lb[i]; j < col; j++ )
						cmp(md,(P)vb[j],(P *)&wb[j]);
				*rp = (Obj)l;
				break;
			default:
				error("remainder : invalid argument");
		}
	}
}

void Psremainder(arg,rp)
NODE arg;
Obj *rp;
{
	Obj a;
	VECT v,w;
	MAT m,l;
	pointer *vb,*wb;
	pointer **mb,**lb;
	unsigned int t,smd;
	int id,i,j,n,row,col;
	Q md,q;

	a = (Obj)ARG0(arg); md = (Q)ARG1(arg);
	if ( !a )
		*rp = 0;
	else {
		id = OID(a);
		switch ( id ) {
			case O_N:
			case O_P:
				cmp(md,(P)a,(P *)rp); break;
			case O_VECT:
				smd = QTOS(md);
				v = (VECT)a; n = v->len; vb = v->body;
				MKVECT(w,n); wb = w->body;
				for ( i = 0; i < n; i++ ) {
					if ( q = (Q)vb[i] ) {
						t = (unsigned int)rem(NM(q),smd);
						if ( SGN(q) < 0 )
							t = (smd - t) % smd;
						UTOQ(t,q);
					}
					wb[i] = (pointer)q;
				}
				*rp = (Obj)w;
				break;
			case O_MAT:
				m = (MAT)a; row = m->row; col = m->col; mb = m->body;
				MKMAT(l,row,col); lb = l->body;
				for ( i = 0; i < row; i++ )
					for ( j = 0, vb = mb[i], wb = lb[i]; j < col; j++ )
						cmp(md,(P)vb[j],(P *)&wb[j]);
				*rp = (Obj)l;
				break;
			default:
				error("remainder : invalid argument");
		}
	}
}

void Psize(arg,rp)
NODE arg;
LIST *rp;
{

	int n,m;
	Q q;
	NODE t,s;

	if ( !ARG0(arg) )
		 t = 0;
	else {
		switch (OID(ARG0(arg))) {
			case O_VECT:
				n = ((VECT)ARG0(arg))->len;
				STOQ(n,q); MKNODE(t,q,0);
				break;
			case O_MAT:
				n = ((MAT)ARG0(arg))->row; m = ((MAT)ARG0(arg))->col;
				STOQ(m,q); MKNODE(s,q,0); STOQ(n,q); MKNODE(t,q,s);
				break;
			default:
				error("size : invalid argument"); break;
		}
	}
	MKLIST(*rp,t);
}

void Pdet(arg,rp)
NODE arg;
P *rp;
{
	MAT m;
	int n,i,j,mod;
	P d;
	P **mat,**w;

	m = (MAT)ARG0(arg);
	asir_assert(m,O_MAT,"det");
	if ( m->row != m->col )
		error("det : non-square matrix");
	else if ( argc(arg) == 1 )
		detp(CO,(P **)BDY(m),m->row,rp);
	else {
		n = m->row; mod = QTOS((Q)ARG1(arg)); mat = (P **)BDY(m);
		w = (P **)almat_pointer(n,n);
		for ( i = 0; i < n; i++ )
			for ( j = 0; j < n; j++ )
				ptomp(mod,mat[i][j],&w[i][j]);
		detmp(CO,mod,w,n,&d);
		mptop(d,rp);
	}
}

/*
	input : a row x col matrix A
		A[I] <-> A[I][0]*x_0+A[I][1]*x_1+...

	output : [B,R,C]
		B : a rank(A) x col-rank(A) matrix
		R : a vector of length rank(A)
		C : a vector of length col-rank(A)
		B[I] <-> x_{R[I]}+B[I][0]x_{C[0]}+B[I][1]x_{C[1]}+...
*/

void Pgeneric_gauss_elim_mod(arg,rp)
NODE arg;
LIST *rp;
{
	NODE n0;
	MAT m,mat;
	VECT rind,cind;
	Q **tmat;
	int **wmat;
	Q *rib,*cib;
	int *colstat;
	Q q;
	int md,i,j,k,l,row,col,t,n,rank;

	asir_assert(ARG0(arg),O_MAT,"generic_gauss_elim_mod");
	asir_assert(ARG1(arg),O_N,"generic_gauss_elim_mod");
	m = (MAT)ARG0(arg); md = QTOS((Q)ARG1(arg));
	row = m->row; col = m->col; tmat = (Q **)m->body;
	wmat = (int **)almat(row,col);
	colstat = (int *)MALLOC_ATOMIC(col*sizeof(int));
	for ( i = 0; i < row; i++ )
		for ( j = 0; j < col; j++ )
			if ( q = (Q)tmat[i][j] ) {
				t = rem(NM(q),md);
				if ( t && SGN(q) < 0 )
					t = (md - t) % md;
				wmat[i][j] = t;
			} else
				wmat[i][j] = 0;
	rank = generic_gauss_elim_mod(wmat,row,col,md,colstat);

	MKMAT(mat,rank,col-rank);
	tmat = (Q **)mat->body;
	for ( i = 0; i < rank; i++ )
		for ( j = k = 0; j < col; j++ )
			if ( !colstat[j] ) {
				UTOQ(wmat[i][j],tmat[i][k]); k++;
			}

	MKVECT(rind,rank);
	MKVECT(cind,col-rank);
	rib = (Q *)rind->body; cib = (Q *)cind->body;
	for ( j = k = l = 0; j < col; j++ )
		if ( colstat[j] ) {
			STOQ(j,rib[k]); k++;
		} else {
			STOQ(j,cib[l]); l++;
		}
	n0 = mknode(3,mat,rind,cind);
	MKLIST(*rp,n0);
}

void Pleqm(arg,rp)
NODE arg;
VECT *rp;
{
	MAT m;
	VECT vect;
	pointer **mat;
	Q *v;
	Q q;
	int **wmat;
	int md,i,j,row,col,t,n,status;

	asir_assert(ARG0(arg),O_MAT,"leqm");
	asir_assert(ARG1(arg),O_N,"leqm");
	m = (MAT)ARG0(arg); md = QTOS((Q)ARG1(arg));
	row = m->row; col = m->col; mat = m->body;
	wmat = (int **)almat(row,col);
	for ( i = 0; i < row; i++ )
		for ( j = 0; j < col; j++ )
			if ( q = (Q)mat[i][j] ) {
				t = rem(NM(q),md);
				if ( SGN(q) < 0 )
					t = (md - t) % md;
				wmat[i][j] = t;
			} else
				wmat[i][j] = 0;
	status = gauss_elim_mod(wmat,row,col,md);
	if ( status < 0 )
		*rp = 0;
	else if ( status > 0 )
		*rp = (VECT)ONE;
	else {
		n = col - 1;
		MKVECT(vect,n);
		for ( i = 0, v = (Q *)vect->body; i < n; i++ ) {
			t = (md-wmat[i][n])%md; STOQ(t,v[i]);
		}
		*rp = vect;
	}
}

int gauss_elim_mod(mat,row,col,md)
int **mat;
int row,col,md;
{
	int i,j,k,inv,a,n;
	int *t,*pivot;

	n = col - 1;
	for ( j = 0; j < n; j++ ) {
		for ( i = j; i < row && !mat[i][j]; i++ );
		if ( i == row )
			return 1;
		if ( i != j ) {
			t = mat[i]; mat[i] = mat[j]; mat[j] = t;
		}
		pivot = mat[j];
		inv = invm(pivot[j],md);
		for ( k = j; k <= n; k++ ) {
/*			pivot[k] = dmar(pivot[k],inv,0,md); */
			DMAR(pivot[k],inv,0,md,pivot[k])
		}
		for ( i = 0; i < row; i++ ) {
			t = mat[i];
			if ( i != j && (a = t[j]) )
				for ( k = j, a = md - a; k <= n; k++ ) {
/*					t[k] = dmar(pivot[k],a,t[k],md); */
					DMAR(pivot[k],a,t[k],md,t[k])
				}
		}
	}
	for ( i = n; i < row && !mat[i][n]; i++ );
	if ( i == row )
		return 0;
	else
		return -1;
}

struct oEGT eg_mod,eg_elim,eg_elim1,eg_elim2,eg_chrem,eg_gschk,eg_intrat,eg_symb;

int generic_gauss_elim(mat,nm,dn,rindp,cindp)
MAT mat;
MAT *nm;
Q *dn;
int **rindp,**cindp;
{
	int **wmat;
	Q **bmat;
	N **tmat;
	Q *bmi;
	N *tmi;
	Q q;
	int *wmi;
	int *colstat,*wcolstat,*rind,*cind;
	int row,col,ind,md,i,j,k,l,t,t1,rank,rank0,inv;
	N m1,m2,m3,s,u;
	MAT r,crmat;
	struct oEGT tmp0,tmp1;
	struct oEGT eg_mod_split,eg_elim_split,eg_chrem_split;
	struct oEGT eg_intrat_split,eg_gschk_split;
	int ret;

	init_eg(&eg_mod_split); init_eg(&eg_chrem_split);
	init_eg(&eg_elim_split); init_eg(&eg_intrat_split);
	init_eg(&eg_gschk_split);
	bmat = (Q **)mat->body;
	row = mat->row; col = mat->col;
	wmat = (int **)almat(row,col);
	colstat = (int *)MALLOC_ATOMIC(col*sizeof(int));
	wcolstat = (int *)MALLOC_ATOMIC(col*sizeof(int));
	for ( ind = 0; ; ind++ ) {
		if ( Print ) {
			fprintf(asir_out,"."); fflush(asir_out);
		}
		md = lprime[ind];
		get_eg(&tmp0);
		for ( i = 0; i < row; i++ )
			for ( j = 0, bmi = bmat[i], wmi = wmat[i]; j < col; j++ )
				if ( q = (Q)bmi[j] ) {
					t = rem(NM(q),md);
					if ( t && SGN(q) < 0 )
						t = (md - t) % md;
					wmi[j] = t;
				} else
					wmi[j] = 0;
		get_eg(&tmp1);
		add_eg(&eg_mod,&tmp0,&tmp1);
		add_eg(&eg_mod_split,&tmp0,&tmp1);
		get_eg(&tmp0);
		rank = generic_gauss_elim_mod(wmat,row,col,md,wcolstat);
		get_eg(&tmp1);
		add_eg(&eg_elim,&tmp0,&tmp1);
		add_eg(&eg_elim_split,&tmp0,&tmp1);
		if ( !ind ) {
RESET:
			UTON(md,m1);
			rank0 = rank;
			bcopy(wcolstat,colstat,col*sizeof(int));
			MKMAT(crmat,rank,col-rank);
			MKMAT(r,rank,col-rank); *nm = r;
			tmat = (N **)crmat->body;
			for ( i = 0; i < rank; i++ )
				for ( j = k = 0, tmi = tmat[i], wmi = wmat[i]; j < col; j++ )
					if ( !colstat[j] ) {
						UTON(wmi[j],tmi[k]); k++;
					}
		} else {
			if ( rank < rank0 ) {
				if ( Print ) {
					fprintf(asir_out,"lower rank matrix; continuing...\n");
					fflush(asir_out);
				}
				continue;
			} else if ( rank > rank0 ) {
				if ( Print ) {
					fprintf(asir_out,"higher rank matrix; resetting...\n");
					fflush(asir_out);
				}
				goto RESET;
			} else {
				for ( j = 0; (j<col) && (colstat[j]==wcolstat[j]); j++ );
				if ( j < col ) {
					if ( Print ) {
						fprintf(asir_out,"inconsitent colstat; resetting...\n");
						fflush(asir_out);
					}
					goto RESET;
				}
			}

			get_eg(&tmp0);
			inv = invm(rem(m1,md),md);
			UTON(md,m2); muln(m1,m2,&m3);
			for ( i = 0; i < rank; i++ )			
				for ( j = k = 0, tmi = tmat[i], wmi = wmat[i]; j < col; j++ )
					if ( !colstat[j] ) {
						if ( tmi[k] ) {
						/* f3 = f1+m1*(m1 mod m2)^(-1)*(f2 - f1 mod m2) */
							t = rem(tmi[k],md);
							if ( wmi[j] >= t )
								t = wmi[j]-t;
							else
								t = md-(t-wmi[j]);
							DMAR(t,inv,0,md,t1)
							UTON(t1,u);
							muln(m1,u,&s);
							addn(tmi[k],s,&u); tmi[k] = u;
						} else if ( wmi[j] ) {
						/* f3 = m1*(m1 mod m2)^(-1)*f2 */
							DMAR(wmi[j],inv,0,md,t)
							UTON(t,u);
							muln(m1,u,&s); tmi[k] = s;
						}
						k++;
					}
			m1 = m3;
			get_eg(&tmp1);
			add_eg(&eg_chrem,&tmp0,&tmp1);
			add_eg(&eg_chrem_split,&tmp0,&tmp1);

			get_eg(&tmp0);
			ret = intmtoratm(crmat,m1,*nm,dn);
			get_eg(&tmp1);
			add_eg(&eg_intrat,&tmp0,&tmp1);
			add_eg(&eg_intrat_split,&tmp0,&tmp1);
			if ( ret ) {
				*rindp = rind = (int *)MALLOC_ATOMIC(rank*sizeof(int));
				*cindp = cind = (int *)MALLOC_ATOMIC((col-rank)*sizeof(int));
				for ( j = k = l = 0; j < col; j++ )
					if ( colstat[j] )
						rind[k++] = j;	
					else
						cind[l++] = j;
				get_eg(&tmp0);
				if ( gensolve_check(mat,*nm,*dn,rind,cind) ) {
					get_eg(&tmp1);
					add_eg(&eg_gschk,&tmp0,&tmp1);
					add_eg(&eg_gschk_split,&tmp0,&tmp1);
					if ( Print ) {
						print_eg("Mod",&eg_mod_split);
						print_eg("Elim",&eg_elim_split);
						print_eg("ChRem",&eg_chrem_split);
						print_eg("IntRat",&eg_intrat_split);
						print_eg("Check",&eg_gschk_split);
						fflush(asir_out);
					}
					return rank;
				}
			}
		}
	}
}

int generic_gauss_elim_hensel(mat,nmmat,dn,rindp,cindp)
MAT mat;
MAT *nmmat;
Q *dn;
int **rindp,**cindp;
{
	MAT bmat,xmat;
	Q **a0,**a,**b,**x,**nm;
	Q *ai,*bi,*xi;
	int row,col;
	int **w;
	int *wi;
	int **wc;
	Q mdq,q,s,u;
	N tn;
	int ind,md,i,j,k,l,li,ri,rank;
	unsigned int t;
	int *cinfo,*rinfo;
	int *rind,*cind;
	int count;
	struct oEGT eg_mul,eg_inv,tmp0,tmp1;

	a0 = (Q **)mat->body;
	row = mat->row; col = mat->col;
	w = (int **)almat(row,col);
	for ( ind = 0; ; ind++ ) {
		md = lprime[ind];
		STOQ(md,mdq);
		for ( i = 0; i < row; i++ )
			for ( j = 0, ai = a0[i], wi = w[i]; j < col; j++ )
				if ( q = (Q)ai[j] ) {
					t = rem(NM(q),md);
					if ( t && SGN(q) < 0 )
						t = (md - t) % md;
					wi[j] = t;
				} else
					wi[j] = 0;

		rank = find_lhs_and_lu_mod(w,row,col,md,&rinfo,&cinfo);
		a = (Q **)almat_pointer(rank,rank); /* lhs mat */
		MKMAT(bmat,rank,col-rank); b = (Q **)bmat->body; /* lhs mat */
		for ( j = li = ri = 0; j < col; j++ )
			if ( cinfo[j] ) {
				/* the column is in lhs */
				for ( i = 0; i < rank; i++ ) {
					w[i][li] = w[i][j];
					a[i][li] = a0[rinfo[i]][j];
				}
				li++;
			} else {
				/* the column is in rhs */
				for ( i = 0; i < rank; i++ )
					b[i][ri] = a0[rinfo[i]][j];
				ri++;
			}

			/* solve Ax+B=0; A: rank x rank, B: rank x ri */
			MKMAT(xmat,rank,ri); x = (Q **)(xmat)->body;
			MKMAT(*nmmat,rank,ri); nm = (Q **)(*nmmat)->body;
			/* use the right part of w as work area */
			/* ri = col - rank */
			wc = (int **)almat(rank,ri);
			for ( i = 0; i < rank; i++ )
				wc[i] = w[i]+rank;
			*rindp = rind = (int *)MALLOC_ATOMIC(rank*sizeof(int));
			*cindp = cind = (int *)MALLOC_ATOMIC((ri)*sizeof(int));

			init_eg(&eg_mul); init_eg(&eg_inv);
			for ( q = ONE, count = 0; ; count++ ) {
				fprintf(stderr,".");
				/* wc = -b mod md */
				for ( i = 0; i < rank; i++ )
					for ( j = 0, bi = b[i], wi = wc[i]; j < ri; j++ )
						if ( u = (Q)bi[j] ) {
							t = rem(NM(u),md);
							if ( t && SGN(u) > 0 )
								t = (md - t) % md;
							wi[j] = t;
						} else
							wi[j] = 0;
				/* wc = A^(-1)wc; wc is normalized */
				get_eg(&tmp0);
				solve_by_lu_mod(w,rank,md,wc,ri);
				get_eg(&tmp1);
				add_eg(&eg_inv,&tmp0,&tmp1);
				/* x = x-q*wc */
				for ( i = 0; i < rank; i++ )
					for ( j = 0, xi = x[i], wi = wc[i]; j < ri; j++ ) {
						STOQ(wi[j],u); mulq(q,u,&s);
						subq(xi[j],s,&u); xi[j] = u;
					}
				get_eg(&tmp0);
				for ( i = 0; i < rank; i++ )
					for ( j = 0; j < ri; j++ ) {
						inner_product_mat_int_mod(a,wc,rank,i,j,&u);
						addq(b[i][j],u,&s);
						if ( s ) {
							t = divin(NM(s),md,&tn);
							if ( t )
								error("generic_gauss_elim_hensel:incosistent");	
							NTOQ(tn,SGN(s),b[i][j]);
						} else
							b[i][j] = 0;
					}
				get_eg(&tmp1);
				add_eg(&eg_mul,&tmp0,&tmp1);
				/* q = q*md */
				mulq(q,mdq,&u); q = u;
				if ( !(count % 2) && intmtoratm_q(xmat,NM(q),*nmmat,dn) ) {
					for ( j = k = l = 0; j < col; j++ )
						if ( cinfo[j] )
							rind[k++] = j;	
						else
							cind[l++] = j;
					if ( gensolve_check(mat,*nmmat,*dn,rind,cind) ) {
						fprintf(stderr,"\n");
						print_eg("INV",&eg_inv);
						print_eg("MUL",&eg_mul);
						fflush(asir_out);
						return rank;
					}
				}
			}
	}
}

int f4_nocheck;

int gensolve_check(mat,nm,dn,rind,cind)
MAT mat,nm;
Q dn;
int *rind,*cind;
{
	int row,col,rank,clen,i,j,k,l;
	Q s,t,u;
	Q *w;
	Q *mati,*nmk;

	if ( f4_nocheck )
		return 1;
	row = mat->row; col = mat->col;
	rank = nm->row; clen = nm->col;	
	w = (Q *)MALLOC(clen*sizeof(Q));
	for ( i = 0; i < row; i++ ) {
		mati = (Q *)mat->body[i];
#if 1
		bzero(w,clen*sizeof(Q));
		for ( k = 0; k < rank; k++ )
			for ( l = 0, nmk = (Q *)nm->body[k]; l < clen; l++ ) {
				mulq(mati[rind[k]],nmk[l],&t);
				addq(w[l],t,&s); w[l] = s;
			}
		for ( j = 0; j < clen; j++ ) {
			mulq(dn,mati[cind[j]],&t);
			if ( cmpq(w[j],t) )
				break;
		}
#else
		for ( j = 0; j < clen; j++ ) {
			for ( k = 0, s = 0; k < rank; k++ ) {
				mulq(mati[rind[k]],nm->body[k][j],&t);
				addq(s,t,&u); s = u;
			}
			mulq(dn,mati[cind[j]],&t);
			if ( cmpq(s,t) )
				break;
		}
#endif
		if ( j != clen )
			break;
	}
	if ( i != row )
		return 0;
	else
		return 1;
}

/* assuming 0 < c < m */

int inttorat(c,m,b,sgnp,nmp,dnp)
N c,m,b;
int *sgnp;
N *nmp,*dnp;
{
	Q qq,t,u1,v1,r1,nm;
	N q,r,u2,v2,r2;

	u1 = 0; v1 = ONE; u2 = m; v2 = c;
	while ( cmpn(v2,b) >= 0 ) {
		divn(u2,v2,&q,&r2); u2 = v2; v2 = r2;
		NTOQ(q,1,qq); mulq(qq,v1,&t); subq(u1,t,&r1); u1 = v1; v1 = r1; 
	}
	if ( cmpn(NM(v1),b) >= 0 )
		return 0;
	else {
		*nmp = v2;
		*dnp = NM(v1);
		*sgnp = SGN(v1);
		return 1;
	}
}

/* mat->body = N ** */

int intmtoratm(mat,md,nm,dn)
MAT mat;
N md;
MAT nm;
Q *dn;
{
	N t,s,b;
	Q bound,dn0,dn1,nm1,q,tq;
	int i,j,k,l,row,col;
	Q **rmat;
	N **tmat;
	N *tmi;
	Q *nmk;
	N u,unm,udn;
	int sgn,ret;

	if ( UNIN(md) )
		return 0;
	row = mat->row; col = mat->col;
	bshiftn(md,1,&t);
	isqrt(t,&s);
	bshiftn(s,64,&b);
	if ( !b )
		b = ONEN;
	dn0 = ONE;
	tmat = (N **)mat->body;
	rmat = (Q **)nm->body;
	for ( i = 0; i < row; i++ )
		for ( j = 0, tmi = tmat[i]; j < col; j++ )
			if ( tmi[j] ) {
				muln(tmi[j],NM(dn0),&s);
				remn(s,md,&u);
				ret = inttorat(u,md,b,&sgn,&unm,&udn);
				if ( !ret )
					return 0;
				else {
					NTOQ(unm,sgn,nm1);
					NTOQ(udn,1,dn1);
					if ( !UNIQ(dn1) ) {
						for ( k = 0; k < i; k++ )
							for ( l = 0, nmk = rmat[k]; l < col; l++ ) {
								mulq(nmk[l],dn1,&q); nmk[l] = q;
							}
						for ( l = 0, nmk = rmat[i]; l < j; l++ ) {
							mulq(nmk[l],dn1,&q); nmk[l] = q;
						}
					}
					rmat[i][j] = nm1;
					mulq(dn0,dn1,&q); dn0 = q;
				}
			}
	*dn = dn0;
	return 1;
}

/* mat->body = Q ** */

int intmtoratm_q(mat,md,nm,dn)
MAT mat;
N md;
MAT nm;
Q *dn;
{
	N t,s,b;
	Q bound,dn0,dn1,nm1,q,tq;
	int i,j,k,l,row,col;
	Q **rmat;
	Q **tmat;
	Q *tmi;
	Q *nmk;
	N u,unm,udn;
	int sgn,ret;

	if ( UNIN(md) )
		return 0;
	row = mat->row; col = mat->col;
	bshiftn(md,1,&t);
	isqrt(t,&s);
	bshiftn(s,64,&b);
	if ( !b )
		b = ONEN;
	dn0 = ONE;
	tmat = (Q **)mat->body;
	rmat = (Q **)nm->body;
	for ( i = 0; i < row; i++ )
		for ( j = 0, tmi = tmat[i]; j < col; j++ )
			if ( tmi[j] ) {
				muln(NM(tmi[j]),NM(dn0),&s);
				remn(s,md,&u);
				ret = inttorat(u,md,b,&sgn,&unm,&udn);
				if ( !ret )
					return 0;
				else {
					if ( SGN(tmi[j])<0 )
						sgn = -sgn;
					NTOQ(unm,sgn,nm1);
					NTOQ(udn,1,dn1);
					if ( !UNIQ(dn1) ) {
						for ( k = 0; k < i; k++ )
							for ( l = 0, nmk = rmat[k]; l < col; l++ ) {
								mulq(nmk[l],dn1,&q); nmk[l] = q;
							}
						for ( l = 0, nmk = rmat[i]; l < j; l++ ) {
							mulq(nmk[l],dn1,&q); nmk[l] = q;
						}
					}
					rmat[i][j] = nm1;
					mulq(dn0,dn1,&q); dn0 = q;
				}
			}
	*dn = dn0;
	return 1;
}

#define ONE_STEP1  if ( zzz = *s ) { DMAR(zzz,hc,*tj,md,*tj) } tj++; s++;

void reduce_reducers_mod(mat,row,col,md)
int **mat;
int row,col;
int md;
{
	int i,j,k,l,hc,zzz;
	int *t,*s,*tj,*ind;

	/* reduce the reducers */
	ind = (int *)ALLOCA(row*sizeof(int));
	for ( i = 0; i < row; i++ ) {
		t = mat[i];
		for ( j = 0; j < col && !t[j]; j++ );
		/* register the position of the head term */
		ind[i] = j;
		for ( l = i-1; l >= 0; l-- ) {
			/* reduce mat[i] by mat[l] */
			if ( hc = t[ind[l]] ) {
				/* mat[i] = mat[i]-hc*mat[l] */
				j = ind[l];
				s = mat[l]+j;
				tj = t+j;
				hc = md-hc;
				k = col-j;
				for ( ; k >= 64; k -= 64 ) {
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
					ONE_STEP1 ONE_STEP1 ONE_STEP1 ONE_STEP1
				}
				for ( ; k >= 0; k-- ) {
					if ( zzz = *s ) { DMAR(zzz,hc,*tj,md,*tj) } tj++; s++;
				}
			}
		}
	}
}

/*
	mat[i] : reducers (i=0,...,nred-1)
	         spolys (i=nred,...,row-1)
	mat[0] < mat[1] < ... < mat[nred-1] w.r.t the term order
	1. reduce the reducers
	2. reduce spolys by the reduced reducers
*/

void pre_reduce_mod(mat,row,col,nred,md)
int **mat;
int row,col,nred;
int md;
{
	int i,j,k,l,hc,inv;
	int *t,*s,*tk,*ind;

#if 1
	/* reduce the reducers */
	ind = (int *)ALLOCA(row*sizeof(int));
	for ( i = 0; i < nred; i++ ) {
		/* make mat[i] monic and mat[i] by mat[0],...,mat[i-1] */
		t = mat[i];
		for ( j = 0; j < col && !t[j]; j++ );
		/* register the position of the head term */
		ind[i] = j;
		inv = invm(t[j],md);
		for ( k = j; k < col; k++ )
			if ( t[k] )
				DMAR(t[k],inv,0,md,t[k])
		for ( l = i-1; l >= 0; l-- ) {
			/* reduce mat[i] by mat[l] */
			if ( hc = t[ind[l]] ) {
				/* mat[i] = mat[i]-hc*mat[l] */
				for ( k = ind[l], hc = md-hc, s = mat[l]+k, tk = t+k;
					k < col; k++, tk++, s++ )
					if ( *s )
						DMAR(*s,hc,*tk,md,*tk)
			}
		}
	}
	/* reduce the spolys */
	for ( i = nred; i < row; i++ ) {
		t = mat[i];
		for ( l = nred-1; l >= 0; l-- ) {
			/* reduce mat[i] by mat[l] */
			if ( hc = t[ind[l]] ) {
				/* mat[i] = mat[i]-hc*mat[l] */
				for ( k = ind[l], hc = md-hc, s = mat[l]+k, tk = t+k;
					k < col; k++, tk++, s++ )
					if ( *s )
						DMAR(*s,hc,*tk,md,*tk)
			}
		}
	}
#endif
}
/*
	mat[i] : reducers (i=0,...,nred-1)
	mat[0] < mat[1] < ... < mat[nred-1] w.r.t the term order
*/

void reduce_sp_by_red_mod(sp,redmat,ind,nred,col,md)
int *sp,**redmat;
int *ind;
int nred,col;
int md;
{
	int i,j,k,hc,zzz;
	int *t,*s,*tj;

	/* reduce the spolys by redmat */
	for ( i = nred-1; i >= 0; i-- ) {
		/* reduce sp by redmat[i] */
		if ( hc = sp[ind[i]] ) {
			/* sp = sp-hc*redmat[i] */
			j = ind[i];
			hc = md-hc;
			s = redmat[i]+j;
			tj = sp+j;
			for ( k = col-j; k >= 0; k-- ) {
				if ( zzz = *s ) { DMAR(zzz,hc,*tj,md,*tj) } tj++; s++;
			}
		}
	}
}

#define ONE_STEP2  if ( zzz = *pk ) { DMAR(zzz,a,*tk,md,*tk) } pk++; tk++;

int generic_gauss_elim_mod(mat,row,col,md,colstat)
int **mat;
int row,col,md;
int *colstat;
{
	int i,j,k,l,inv,a,rank,zzz;
	int *t,*pivot,*pk,*tk;

	for ( rank = 0, j = 0; j < col; j++ ) {
		for ( i = rank; i < row && !mat[i][j]; i++ );
		if ( i == row ) {
			colstat[j] = 0;
			continue;
		} else
			colstat[j] = 1;
		if ( i != rank ) {
			t = mat[i]; mat[i] = mat[rank]; mat[rank] = t;
		}
		pivot = mat[rank];
		inv = invm(pivot[j],md);
		for ( k = j, pk = pivot+k; k < col; k++, pk++ )
			if ( *pk ) {
				DMAR(*pk,inv,0,md,*pk)
			}
		for ( i = rank+1; i < row; i++ ) {
			t = mat[i];
			if ( a = t[j] ) {
				a = md - a; pk = pivot+j; tk = t+j;
				k = col-j;
				for ( ; k >= 64; k -= 64 ) {
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
				}
				for ( ; k >= 0; k -- ) {
					if ( zzz = *pk ) { DMAR(zzz,a,*tk,md,*tk) } pk++; tk++;
				}
			}
		}
		rank++;
	}
	for ( j = col-1, l = rank-1; j >= 0; j-- )
		if ( colstat[j] ) {
			pivot = mat[l];
			for ( i = 0; i < l; i++ ) {
				t = mat[i];
				if ( a = t[j] ) {
					a = md-a; pk = pivot+j; tk = t+j;
					k = col-j;
					for ( ; k >= 64; k -= 64 ) {
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					ONE_STEP2 ONE_STEP2 ONE_STEP2 ONE_STEP2
					}
					for ( ; k >= 0; k -- ) {
						if ( zzz = *pk ) { DMAR(zzz,a,*tk,md,*tk) } pk++; tk++;
					}
				}
			}
			l--;
		}
	return rank;
}

/* LU decomposition; a[i][i] = 1/U[i][i] */

int lu_gfmmat(mat,md,perm)
GFMMAT mat;
unsigned int md;
int *perm;
{
	int row,col;
	int i,j,k,l;
	unsigned int *t,*pivot;
	unsigned int **a;
	unsigned int inv,m;

	row = mat->row; col = mat->col;
	a = mat->body;
	bzero(perm,row*sizeof(int));

	for ( i = 0; i < row; i++ )
		perm[i] = i;
	for ( k = 0; k < col; k++ ) {
		for ( i = k; i < row && !a[i][k]; i++ );
		if ( i == row )
			return 0;
		if ( i != k ) {
			j = perm[i]; perm[i] = perm[k]; perm[k] = j;
			t = a[i]; a[i] = a[k]; a[k] = t;
		}
		pivot = a[k];
		pivot[k] = inv = invm(pivot[k],md);
		for ( i = k+1; i < row; i++ ) {
			t = a[i];
			if ( m = t[k] ) {
				DMAR(inv,m,0,md,t[k])
				for ( j = k+1, m = md - t[k]; j < col; j++ )
					if ( pivot[j] ) {
						DMAR(m,pivot[j],t[j],md,t[j])
					}
			}
		}
	}
	return 1;
}

/*
 Input
	a: a row x col matrix
	md : a modulus

 Output:
	return : d = the rank of mat
	a[0..(d-1)][0..(d-1)] : LU decomposition (a[i][i] = 1/U[i][i])
	rinfo: array of length row
	cinfo: array of length col
    i-th row in new a <-> rinfo[i]-th row in old a
	cinfo[j]=1 <=> j-th column is contained in the LU decomp.
*/

int find_lhs_and_lu_mod(a,row,col,md,rinfo,cinfo)
unsigned int **a;
unsigned int md;
int **rinfo,**cinfo;
{
	int i,j,k,l,d;
	int *rp,*cp;
	unsigned int *t,*pivot;
	unsigned int inv,m;

	*rinfo = rp = (int *)MALLOC_ATOMIC(row*sizeof(int));
	*cinfo = cp = (int *)MALLOC_ATOMIC(col*sizeof(int));
	for ( i = 0; i < row; i++ )
		rp[i] = i;
	for ( k = 0, d = 0; k < col; k++ ) {
		for ( i = d; i < row && !a[i][k]; i++ );
		if ( i == row ) {
			cp[k] = 0;
			continue;
		} else
			cp[k] = 1;
		if ( i != d ) {
			j = rp[i]; rp[i] = rp[d]; rp[d] = j;
			t = a[i]; a[i] = a[d]; a[d] = t;
		}
		pivot = a[d];
		pivot[k] = inv = invm(pivot[k],md);
		for ( i = d+1; i < row; i++ ) {
			t = a[i];
			if ( m = t[k] ) {
				DMAR(inv,m,0,md,t[k])
				for ( j = k+1, m = md - t[k]; j < col; j++ )
					if ( pivot[j] ) {
						DMAR(m,pivot[j],t[j],md,t[j])
					}
			}
		}
		d++;
	}
	return d;
}

/*
  Input
	a : n x n matrix; a result of LU-decomposition
	md : modulus
	b : n x l matrix 
 Output
	b = a^(-1)b
 */

void solve_by_lu_mod(a,n,md,b,l)
int **a;
int n;
int md;
int **b;
int l;
{
	unsigned int *y,*c;
	int i,j,k;
	unsigned int t,m,m2;

	y = (int *)MALLOC_ATOMIC(n*sizeof(int));
	c = (int *)MALLOC_ATOMIC(n*sizeof(int));
	m2 = md>>1;
	for ( k = 0; k < l; k++ ) {
		/* copy b[.][k] to c */
		for ( i = 0; i < n; i++ )
			c[i] = (unsigned int)b[i][k];
		/* solve Ly=c */
		for ( i = 0; i < n; i++ ) {
			for ( t = c[i], j = 0; j < i; j++ )
				if ( a[i][j] ) {
					m = md - a[i][j];
					DMAR(m,y[j],t,md,t)
				}
			y[i] = t;
		}
		/* solve Uc=y */
		for ( i = n-1; i >= 0; i-- ) {
			for ( t = y[i], j =i+1; j < n; j++ )
				if ( a[i][j] ) {
					m = md - a[i][j];
					DMAR(m,c[j],t,md,t)
				}
			/* a[i][i] = 1/U[i][i] */
			DMAR(t,a[i][i],0,md,c[i])
		}
		/* copy c to b[.][k] with normalization */
		for ( i = 0; i < n; i++ )
			b[i][k] = (int)(c[i]>m2 ? c[i]-md : c[i]);
	}
}

void Pleqm1(arg,rp)
NODE arg;
VECT *rp;
{
	MAT m;
	VECT vect;
	pointer **mat;
	Q *v;
	Q q;
	int **wmat;
	int md,i,j,row,col,t,n,status;

	asir_assert(ARG0(arg),O_MAT,"leqm1");
	asir_assert(ARG1(arg),O_N,"leqm1");
	m = (MAT)ARG0(arg); md = QTOS((Q)ARG1(arg));
	row = m->row; col = m->col; mat = m->body;
	wmat = (int **)almat(row,col);
	for ( i = 0; i < row; i++ )
		for ( j = 0; j < col; j++ )
			if ( q = (Q)mat[i][j] ) {
				t = rem(NM(q),md);
				if ( SGN(q) < 0 )
					t = (md - t) % md;
				wmat[i][j] = t;
			} else
				wmat[i][j] = 0;
	status = gauss_elim_mod1(wmat,row,col,md);
	if ( status < 0 )
		*rp = 0;
	else if ( status > 0 )
		*rp = (VECT)ONE;
	else {
		n = col - 1;
		MKVECT(vect,n);
		for ( i = 0, v = (Q *)vect->body; i < n; i++ ) {
			t = (md-wmat[i][n])%md; STOQ(t,v[i]);
		}
		*rp = vect;
	}
}

gauss_elim_mod1(mat,row,col,md)
int **mat;
int row,col,md;
{
	int i,j,k,inv,a,n;
	int *t,*pivot;

	n = col - 1;
	for ( j = 0; j < n; j++ ) {
		for ( i = j; i < row && !mat[i][j]; i++ );
		if ( i == row )
			return 1;
		if ( i != j ) {
			t = mat[i]; mat[i] = mat[j]; mat[j] = t;
		}
		pivot = mat[j];
		inv = invm(pivot[j],md);
		for ( k = j; k <= n; k++ )
			pivot[k] = dmar(pivot[k],inv,0,md);
		for ( i = j+1; i < row; i++ ) {
			t = mat[i];
			if ( i != j && (a = t[j]) )
				for ( k = j, a = md - a; k <= n; k++ )
					t[k] = dmar(pivot[k],a,t[k],md);
		}
	}
	for ( i = n; i < row && !mat[i][n]; i++ );
	if ( i == row ) {
		for ( j = n-1; j >= 0; j-- ) {
			for ( i = j-1, a = (md-mat[j][n])%md; i >= 0; i-- ) {
				mat[i][n] = dmar(mat[i][j],a,mat[i][n],md);
				mat[i][j] = 0;
			}
		}
		return 0;
	} else
		return -1;
}

void Pgeninvm(arg,rp)
NODE arg;
LIST *rp;
{
	MAT m;
	pointer **mat;
	Q **tmat;
	Q q;
	unsigned int **wmat;
	int md,i,j,row,col,t,status;
	MAT mat1,mat2;
	NODE node1,node2;

	asir_assert(ARG0(arg),O_MAT,"leqm1");
	asir_assert(ARG1(arg),O_N,"leqm1");
	m = (MAT)ARG0(arg); md = QTOS((Q)ARG1(arg));
	row = m->row; col = m->col; mat = m->body;
	wmat = (unsigned int **)almat(row,col+row);
	for ( i = 0; i < row; i++ ) {
		bzero((char *)wmat[i],(col+row)*sizeof(int));
		for ( j = 0; j < col; j++ )
			if ( q = (Q)mat[i][j] ) {
				t = rem(NM(q),md);
				if ( SGN(q) < 0 )
					t = (md - t) % md;
				wmat[i][j] = t;
			}
		wmat[i][col+i] = 1;
	}
	status = gauss_elim_geninv_mod(wmat,row,col,md);
	if ( status > 0 )
		*rp = 0;
	else {
		MKMAT(mat1,col,row); MKMAT(mat2,row-col,row);
		for ( i = 0, tmat = (Q **)mat1->body; i < col; i++ )
			for ( j = 0; j < row; j++ )
				STOQ(wmat[i][j+col],tmat[i][j]);
		for ( tmat = (Q **)mat2->body; i < row; i++ )
			for ( j = 0; j < row; j++ )
				STOQ(wmat[i][j+col],tmat[i-col][j]);
	 	MKNODE(node2,mat2,0); MKNODE(node1,mat1,node2); MKLIST(*rp,node1);
	}
}

int gauss_elim_geninv_mod(mat,row,col,md)
unsigned int **mat;
int row,col,md;
{
	int i,j,k,inv,a,n,m;
	unsigned int *t,*pivot;

	n = col; m = row+col;
	for ( j = 0; j < n; j++ ) {
		for ( i = j; i < row && !mat[i][j]; i++ );
		if ( i == row )
			return 1;
		if ( i != j ) {
			t = mat[i]; mat[i] = mat[j]; mat[j] = t;
		}
		pivot = mat[j];
		inv = invm(pivot[j],md);
		for ( k = j; k < m; k++ )
			pivot[k] = dmar(pivot[k],inv,0,md);
		for ( i = j+1; i < row; i++ ) {
			t = mat[i];
			if ( a = t[j] )
				for ( k = j, a = md - a; k < m; k++ )
					t[k] = dmar(pivot[k],a,t[k],md);
		}
	}
	for ( j = n-1; j >= 0; j-- ) {
		pivot = mat[j];
		for ( i = j-1; i >= 0; i-- ) {
			t = mat[i];
			if ( a = t[j] )
				for ( k = j, a = md - a; k < m; k++ )
					t[k] = dmar(pivot[k],a,t[k],md);
		}
	}
	return 0;
}

void Psolve_by_lu_gfmmat(arg,rp)
NODE arg;
VECT *rp;
{
	GFMMAT lu;
	Q *perm,*rhs,*v;
	int n,i;
	unsigned int md;
	unsigned int *b,*sol;
	VECT r;

	lu = (GFMMAT)ARG0(arg);
	perm = (Q *)BDY((VECT)ARG1(arg));
	rhs = (Q *)BDY((VECT)ARG2(arg));
	md = (unsigned int)QTOS((Q)ARG3(arg));
	n = lu->col;
	b = (unsigned int *)MALLOC_ATOMIC(n*sizeof(int));
	sol = (unsigned int *)MALLOC_ATOMIC(n*sizeof(int));
	for ( i = 0; i < n; i++ )
		b[i] = QTOS(rhs[QTOS(perm[i])]);
	solve_by_lu_gfmmat(lu,md,b,sol);
	MKVECT(r,n);
	for ( i = 0, v = (Q *)r->body; i < n; i++ )
			STOQ(sol[i],v[i]);
	*rp = r;
}

void solve_by_lu_gfmmat(lu,md,b,x)
GFMMAT lu;
unsigned int md;
unsigned int *b;
unsigned int *x;
{
	int n;
	unsigned int **a;
	unsigned int *y;
	int i,j;
	unsigned int t,m;

	n = lu->col;
	a = lu->body;
	y = (unsigned int *)MALLOC_ATOMIC(n*sizeof(int));
	/* solve Ly=b */
	for ( i = 0; i < n; i++ ) {
		for ( t = b[i], j = 0; j < i; j++ )
			if ( a[i][j] ) {
				m = md - a[i][j];
				DMAR(m,y[j],t,md,t)
			}
		y[i] = t;
	}
	/* solve Ux=y */
	for ( i = n-1; i >= 0; i-- ) {
		for ( t = y[i], j =i+1; j < n; j++ )
			if ( a[i][j] ) {
				m = md - a[i][j];
				DMAR(m,x[j],t,md,t)
			}
		/* a[i][i] = 1/U[i][i] */
		DMAR(t,a[i][i],0,md,x[i])
	}
}

void Plu_gfmmat(arg,rp)
NODE arg;
LIST *rp;
{
	MAT m;
	GFMMAT mm;
	unsigned int md;
	int i,row,col,status;
	int *iperm;
	Q *v;
	VECT perm;
	NODE n0;

	asir_assert(ARG0(arg),O_MAT,"mat_to_gfmmat");
	asir_assert(ARG1(arg),O_N,"mat_to_gfmmat");
	m = (MAT)ARG0(arg); md = (unsigned int)QTOS((Q)ARG1(arg));
	mat_to_gfmmat(m,md,&mm);
	row = m->row;
	col = m->col;
	iperm = (int *)MALLOC_ATOMIC(row*sizeof(int));
	status = lu_gfmmat(mm,md,iperm);	
	if ( !status )
		n0 = 0;
	else {
		MKVECT(perm,row);
		for ( i = 0, v = (Q *)perm->body; i < row; i++ )
			STOQ(iperm[i],v[i]);
		n0 = mknode(2,mm,perm);
	}
	MKLIST(*rp,n0);
}

void Pmat_to_gfmmat(arg,rp)
NODE arg;
GFMMAT *rp;
{
	MAT m;
	unsigned int md;

	asir_assert(ARG0(arg),O_MAT,"mat_to_gfmmat");
	asir_assert(ARG1(arg),O_N,"mat_to_gfmmat");
	m = (MAT)ARG0(arg); md = (unsigned int)QTOS((Q)ARG1(arg));
	mat_to_gfmmat(m,md,rp);	
}

void mat_to_gfmmat(m,md,rp)
MAT m;
unsigned int md;
GFMMAT *rp;
{
	unsigned int **wmat;
	unsigned int t;
	Q **mat;
	Q q;
	int i,j,row,col;

	row = m->row; col = m->col; mat = (Q **)m->body;
	wmat = (unsigned int **)almat(row,col);
	for ( i = 0; i < row; i++ ) {
		bzero((char *)wmat[i],col*sizeof(unsigned int));
		for ( j = 0; j < col; j++ )
			if ( q = mat[i][j] ) {
				t = (unsigned int)rem(NM(q),md);
				if ( SGN(q) < 0 )
					t = (md - t) % md;
				wmat[i][j] = t;
			}
	}
	TOGFMMAT(row,col,wmat,*rp);
}

void Pgeninvm_swap(arg,rp)
NODE arg;
LIST *rp;
{
	MAT m;
	pointer **mat;
	Q **tmat;
	Q *tvect;
	Q q;
	unsigned int **wmat,**invmat;
	int *index;
	unsigned int t,md;
	int i,j,row,col,status;
	MAT mat1;
	VECT vect1;
	NODE node1,node2;

	asir_assert(ARG0(arg),O_MAT,"geninvm_swap");
	asir_assert(ARG1(arg),O_N,"geninvm_swap");
	m = (MAT)ARG0(arg); md = QTOS((Q)ARG1(arg));
	row = m->row; col = m->col; mat = m->body;
	wmat = (unsigned int **)almat(row,col+row);
	for ( i = 0; i < row; i++ ) {
		bzero((char *)wmat[i],(col+row)*sizeof(int));
		for ( j = 0; j < col; j++ )
			if ( q = (Q)mat[i][j] ) {
				t = (unsigned int)rem(NM(q),md);
				if ( SGN(q) < 0 )
					t = (md - t) % md;
				wmat[i][j] = t;
			}
		wmat[i][col+i] = 1;
	}
	status = gauss_elim_geninv_mod_swap(wmat,row,col,md,&invmat,&index);
	if ( status > 0 )
		*rp = 0;
	else {
		MKMAT(mat1,col,col);
		for ( i = 0, tmat = (Q **)mat1->body; i < col; i++ )
			for ( j = 0; j < col; j++ )
				UTOQ(invmat[i][j],tmat[i][j]);
		MKVECT(vect1,row);
		for ( i = 0, tvect = (Q *)vect1->body; i < row; i++ )
			STOQ(index[i],tvect[i]);
	 	MKNODE(node2,vect1,0); MKNODE(node1,mat1,node2); MKLIST(*rp,node1);
	}
}

gauss_elim_geninv_mod_swap(mat,row,col,md,invmatp,indexp)
unsigned int **mat;
int row,col;
unsigned int md;
unsigned int ***invmatp;
int **indexp;
{
	int i,j,k,inv,a,n,m;
	unsigned int *t,*pivot,*s;
	int *index;
	unsigned int **invmat;

	n = col; m = row+col;
	*indexp = index = (int *)MALLOC_ATOMIC(row*sizeof(int));
	for ( i = 0; i < row; i++ )
		index[i] = i;
	for ( j = 0; j < n; j++ ) {
		for ( i = j; i < row && !mat[i][j]; i++ );
		if ( i == row ) {
			*indexp = 0; *invmatp = 0; return 1;
		}
		if ( i != j ) {
			t = mat[i]; mat[i] = mat[j]; mat[j] = t;
			k = index[i]; index[i] = index[j]; index[j] = k;	
		}
		pivot = mat[j];
		inv = (unsigned int)invm(pivot[j],md);
		for ( k = j; k < m; k++ )
			if ( pivot[k] )
				pivot[k] = (unsigned int)dmar(pivot[k],inv,0,md);
		for ( i = j+1; i < row; i++ ) {
			t = mat[i];
			if ( a = t[j] )
				for ( k = j, a = md - a; k < m; k++ )
					if ( pivot[k] )
						t[k] = dmar(pivot[k],a,t[k],md);
		}
	}
	for ( j = n-1; j >= 0; j-- ) {
		pivot = mat[j];
		for ( i = j-1; i >= 0; i-- ) {
			t = mat[i];
			if ( a = t[j] )
				for ( k = j, a = md - a; k < m; k++ )
					if ( pivot[k] )
						t[k] = dmar(pivot[k],a,t[k],md);
		}
	}
	*invmatp = invmat = (unsigned int **)almat(col,col);
	for ( i = 0; i < col; i++ )
		for ( j = 0, s = invmat[i], t = mat[i]; j < col; j++ )
			s[j] = t[col+index[j]];
	return 0;
}

void _addn(N,N,N);
int _subn(N,N,N);
void _muln(N,N,N);

void inner_product_int(a,b,n,r)
Q *a,*b;
int n;
Q *r;
{
	int la,lb,i;
	int sgn,sgn1;
	N wm,wma,sum,t;

	for ( la = lb = 0, i = 0; i < n; i++ ) {
		if ( a[i] )
			if ( DN(a[i]) )
				error("inner_product_int : invalid argument");
			else
				la = MAX(PL(NM(a[i])),la);
		if ( b[i] )
			if ( DN(b[i]) )
				error("inner_product_int : invalid argument");
			else
				lb = MAX(PL(NM(b[i])),lb);
	}
	sgn = 0;
	sum= NALLOC(la+lb+2);
	bzero((char *)sum,(la+lb+3)*sizeof(unsigned int));
	wm = NALLOC(la+lb+2);
	wma = NALLOC(la+lb+2);
	for ( i = 0; i < n; i++ ) {
		if ( !a[i] || !b[i] )
			continue;
		_muln(NM(a[i]),NM(b[i]),wm);
		sgn1 = SGN(a[i])*SGN(b[i]);
		if ( !sgn ) {
			sgn = sgn1;
			t = wm; wm = sum; sum = t;
		} else if ( sgn == sgn1 ) {
			_addn(sum,wm,wma);
			if ( !PL(wma) )
				sgn = 0;
			t = wma; wma = sum; sum = t;
		} else {
			/* sgn*sum+sgn1*wm = sgn*(sum-wm) */
			sgn *= _subn(sum,wm,wma);
			t = wma; wma = sum; sum = t;
		}
	}
	GC_free(wm);
	GC_free(wma);
	if ( !sgn ) {
		GC_free(sum);
		*r = 0;
	} else
		NTOQ(sum,sgn,*r);
}

/* (k,l) element of a*b where a: .x n matrix, b: n x . integer matrix */

void inner_product_mat_int_mod(a,b,n,k,l,r)
Q **a;
int **b;
int n,k,l;
Q *r;
{
	int la,lb,i;
	int sgn,sgn1;
	N wm,wma,sum,t;
	Q aki;
	int bil,bilsgn;
	struct oN tn;

	for ( la = 0, i = 0; i < n; i++ ) {
		if ( aki = a[k][i] )
			if ( DN(aki) )
				error("inner_product_int : invalid argument");
			else
				la = MAX(PL(NM(aki)),la);
	}
	lb = 1;
	sgn = 0;
	sum= NALLOC(la+lb+2);
	bzero((char *)sum,(la+lb+3)*sizeof(unsigned int));
	wm = NALLOC(la+lb+2);
	wma = NALLOC(la+lb+2);
	for ( i = 0; i < n; i++ ) {
		if ( !(aki = a[k][i]) || !(bil = b[i][l]) )
			continue;
		tn.p = 1;
		if ( bil > 0 ) {
			tn.b[0] = bil; bilsgn = 1;
		} else {
			tn.b[0] = -bil; bilsgn = -1;
		}
		_muln(NM(aki),&tn,wm);
		sgn1 = SGN(aki)*bilsgn;
		if ( !sgn ) {
			sgn = sgn1;
			t = wm; wm = sum; sum = t;
		} else if ( sgn == sgn1 ) {
			_addn(sum,wm,wma);
			if ( !PL(wma) )
				sgn = 0;
			t = wma; wma = sum; sum = t;
		} else {
			/* sgn*sum+sgn1*wm = sgn*(sum-wm) */
			sgn *= _subn(sum,wm,wma);
			t = wma; wma = sum; sum = t;
		}
	}
	GC_free(wm);
	GC_free(wma);
	if ( !sgn ) {
		GC_free(sum);
		*r = 0;
	} else
		NTOQ(sum,sgn,*r);
}

void Pmul_mat_vect_int(arg,rp)
NODE arg;
VECT *rp;
{
	MAT mat;
	VECT vect,r;
	int row,col,i;

	mat = (MAT)ARG0(arg);
	vect = (VECT)ARG1(arg);
	row = mat->row;
	col = mat->col;
	MKVECT(r,row);
	for ( i = 0; i < row; i++ )
		inner_product_int(mat->body[i],vect->body,col,&r->body[i]);
	*rp = r;
}

void Pnbpoly_up2(arg,rp)
NODE arg;
GF2N *rp;
{
	int m,type,ret;
	UP2 r;

	m = QTOS((Q)ARG0(arg));
	type = QTOS((Q)ARG1(arg));
	ret = generate_ONB_polynomial(&r,m,type);
	if ( ret == 0 )
		MKGF2N(r,*rp);
	else
		*rp = 0;
}

void Px962_irredpoly_up2(arg,rp)
NODE arg;
GF2N *rp;
{
	int m,type,ret,w;
	GF2N prev;
	UP2 r;

	m = QTOS((Q)ARG0(arg));
	prev = (GF2N)ARG1(arg);
	if ( !prev ) {
		w = (m>>5)+1; NEWUP2(r,w); r->w = 0;
		bzero((char *)r->b,w*sizeof(unsigned int));
	} else {
		r = prev->body;
		if ( degup2(r) != m ) {
			w = (m>>5)+1; NEWUP2(r,w); r->w = 0;
			bzero((char *)r->b,w*sizeof(unsigned int));
		}
	}
	ret = _generate_irreducible_polynomial(r,m,type);
	if ( ret == 0 )
		MKGF2N(r,*rp);
	else
		*rp = 0;
}

void Pirredpoly_up2(arg,rp)
NODE arg;
GF2N *rp;
{
	int m,type,ret,w;
	GF2N prev;
	UP2 r;

	m = QTOS((Q)ARG0(arg));
	prev = (GF2N)ARG1(arg);
	if ( !prev ) {
		w = (m>>5)+1; NEWUP2(r,w); r->w = 0;
		bzero((char *)r->b,w*sizeof(unsigned int));
	} else {
		r = prev->body;
		if ( degup2(r) != m ) {
			w = (m>>5)+1; NEWUP2(r,w); r->w = 0;
			bzero((char *)r->b,w*sizeof(unsigned int));
		}
	}
	ret = _generate_good_irreducible_polynomial(r,m,type);
	if ( ret == 0 )
		MKGF2N(r,*rp);
	else
		*rp = 0;
}

/*
 * f = type 'type' normal polynomial of degree m if exists
 * IEEE P1363 A.7.2
 *
 * return value : 0  --- exists
 *                1  --- does not exist
 *                -1 --- failure (memory allocation error)
 */

int generate_ONB_polynomial(UP2 *rp,int m,int type)
{
	int i,r;
	int w;
	UP2 f,f0,f1,f2,t;

	w = (m>>5)+1;
	switch ( type ) {
		case 1:
			if ( !TypeT_NB_check(m,1) ) return 1;
			NEWUP2(f,w); *rp = f; f->w = w;
			/* set all the bits */
			for ( i = 0; i < w; i++ )
				f->b[i] = 0xffffffff;
			/* mask the top word if necessary */
			if ( r = (m+1)&31 )
				f->b[w-1] &= (1<<r)-1;
			return 0;
			break;
		case 2:
			if ( !TypeT_NB_check(m,2) ) return 1;
			NEWUP2(f,w); *rp = f;
			W_NEWUP2(f0,w);
			W_NEWUP2(f1,w);
			W_NEWUP2(f2,w);

			/* recursion for genrating Type II normal polynomial */

			/* f0 = 1, f1 = t+1 */
			f0->w = 1; f0->b[0] = 1; 
			f1->w = 1; f1->b[0] = 3;
			for ( i = 2; i <= m; i++ ) {
				/* f2 = t*f1+f0 */
				_bshiftup2(f1,-1,f2);
				_addup2_destructive(f2,f0);
				/* cyclic change of the variables */
				t = f0; f0 = f1; f1 = f2; f2 = t;
			}
			_copyup2(f1,f);
			return 0;
			break;
		default:
			return -1;
			break;
		}
}

/*
 * f = an irreducible trinomial or pentanomial of degree d 'after' f
 * return value : 0  --- exists
 *                1  --- does not exist (exhaustion)
 */

int _generate_irreducible_polynomial(UP2 f,int d)
{
	int ret,i,j,k,nz,i0,j0,k0;
	int w;
	unsigned int *fd;

	/*
	 * if f = x^d+x^i+1 then i0 <- i, j0 <- 0, k0 <-0.
	 * if f = x^d+x^k+x^j+x^i+1 (k>j>i) then i0 <- i, j0 <- j, k0 <-k.
	 * otherwise i0,j0,k0 is set to 0.
	 */

	fd = f->b;
	w = (d>>5)+1;
	if ( f->w && (d==degup2(f)) ) {
		for ( nz = 0, i = d; i >= 0; i-- )
			if ( fd[i>>5]&(1<<(i&31)) ) nz++;
		switch ( nz ) {
			case 3:
				for ( i0 = 1; !(fd[i0>>5]&(1<<(i0&31))) ; i0++ );
				/* reset i0-th bit */
				fd[i0>>5] &= ~(1<<(i0&31));
				j0 = k0 = 0;
				break;
			case 5:
				for ( i0 = 1; !(fd[i0>>5]&(1<<(i0&31))) ; i0++ );
				/* reset i0-th bit */
				fd[i0>>5] &= ~(1<<(i0&31));
				for ( j0 = i0+1; !(fd[j0>>5]&(1<<(j0&31))) ; j0++ );
				/* reset j0-th bit */
				fd[j0>>5] &= ~(1<<(j0&31));
				for ( k0 = j0+1; !(fd[k0>>5]&(1<<(k0&31))) ; k0++ );
				/* reset k0-th bit */
				fd[k0>>5] &= ~(1<<(k0&31));
				break;
			default:
				f->w = 0; break;
		}
	} else 
		f->w = 0;

	if ( !f->w ) {
		fd = f->b;
		f->w = w; fd[0] |= 1; fd[d>>5] |= (1<<(d&31));
		i0 = j0 = k0 = 0;
	}
	/* if j0 > 0 then f is already a pentanomial */
	if ( j0 > 0 ) goto PENTA;

	/* searching for an irreducible trinomial */

	for ( i = 1; 2*i <= d; i++ ) {
		/* skip the polynomials 'before' f */
		if ( i < i0 ) continue;
		if ( i == i0 ) { i0 = 0; continue; }
		/* set i-th bit */
		fd[i>>5] |= (1<<(i&31));
		ret = irredcheck_dddup2(f);
		if ( ret == 1 ) return 0;
		/* reset i-th bit */
		fd[i>>5] &= ~(1<<(i&31));
	}

	/* searching for an irreducible pentanomial */
PENTA:
	for ( i = 1; i < d; i++ ) {
		/* skip the polynomials 'before' f */
		if ( i < i0 ) continue;
		if ( i == i0 ) i0 = 0;
		/* set i-th bit */
		fd[i>>5] |= (1<<(i&31));
		for ( j = i+1; j < d; j++ ) {
			/* skip the polynomials 'before' f */
			if ( j < j0 ) continue;
			if ( j == j0 ) j0 = 0;
			/* set j-th bit */
			fd[j>>5] |= (1<<(j&31));
			for ( k = j+1; k < d; k++ ) {
				/* skip the polynomials 'before' f */
				if ( k < k0 ) continue;
				else if ( k == k0 ) { k0 = 0; continue; }
				/* set k-th bit */
				fd[k>>5] |= (1<<(k&31));
				ret = irredcheck_dddup2(f);
				if ( ret == 1 ) return 0;
				/* reset k-th bit */
				fd[k>>5] &= ~(1<<(k&31));
			}
			/* reset j-th bit */
			fd[j>>5] &= ~(1<<(j&31));
		}
		/* reset i-th bit */
		fd[i>>5] &= ~(1<<(i&31));
	}
	/* exhausted */
	return 1;
}

/*
 * f = an irreducible trinomial or pentanomial of degree d 'after' f
 * 
 * searching strategy:
 *   trinomial x^d+x^i+1:
 *         i is as small as possible.
 *   trinomial x^d+x^i+x^j+x^k+1: 
 *         i is as small as possible.
 *         For such i, j is as small as possible.
 *         For such i and j, 'k' is as small as possible.
 *
 * return value : 0  --- exists
 *                1  --- does not exist (exhaustion)
 */

int _generate_good_irreducible_polynomial(UP2 f,int d)
{
	int ret,i,j,k,nz,i0,j0,k0;
	int w;
	unsigned int *fd;

	/*
	 * if f = x^d+x^i+1 then i0 <- i, j0 <- 0, k0 <-0.
	 * if f = x^d+x^k+x^j+x^i+1 (k>j>i) then i0 <- i, j0 <- j, k0 <-k.
	 * otherwise i0,j0,k0 is set to 0.
	 */

	fd = f->b;
	w = (d>>5)+1;
	if ( f->w && (d==degup2(f)) ) {
		for ( nz = 0, i = d; i >= 0; i-- )
			if ( fd[i>>5]&(1<<(i&31)) ) nz++;
		switch ( nz ) {
			case 3:
				for ( i0 = 1; !(fd[i0>>5]&(1<<(i0&31))) ; i0++ );
				/* reset i0-th bit */
				fd[i0>>5] &= ~(1<<(i0&31));
				j0 = k0 = 0;
				break;
			case 5:
				for ( i0 = 1; !(fd[i0>>5]&(1<<(i0&31))) ; i0++ );
				/* reset i0-th bit */
				fd[i0>>5] &= ~(1<<(i0&31));
				for ( j0 = i0+1; !(fd[j0>>5]&(1<<(j0&31))) ; j0++ );
				/* reset j0-th bit */
				fd[j0>>5] &= ~(1<<(j0&31));
				for ( k0 = j0+1; !(fd[k0>>5]&(1<<(k0&31))) ; k0++ );
				/* reset k0-th bit */
				fd[k0>>5] &= ~(1<<(k0&31));
				break;
			default:
				f->w = 0; break;
		}
	} else 
		f->w = 0;

	if ( !f->w ) {
		fd = f->b;
		f->w = w; fd[0] |= 1; fd[d>>5] |= (1<<(d&31));
		i0 = j0 = k0 = 0;
	}
	/* if j0 > 0 then f is already a pentanomial */
	if ( j0 > 0 ) goto PENTA;

	/* searching for an irreducible trinomial */

	for ( i = 1; 2*i <= d; i++ ) {
		/* skip the polynomials 'before' f */
		if ( i < i0 ) continue;
		if ( i == i0 ) { i0 = 0; continue; }
		/* set i-th bit */
		fd[i>>5] |= (1<<(i&31));
		ret = irredcheck_dddup2(f);
		if ( ret == 1 ) return 0;
		/* reset i-th bit */
		fd[i>>5] &= ~(1<<(i&31));
	}

	/* searching for an irreducible pentanomial */
PENTA:
	for ( i = 3; i < d; i++ ) {
		/* skip the polynomials 'before' f */
		if ( i < i0 ) continue;
		if ( i == i0 ) i0 = 0;
		/* set i-th bit */
		fd[i>>5] |= (1<<(i&31));
 		for ( j = 2; j < i; j++ ) {
			/* skip the polynomials 'before' f */
			if ( j < j0 ) continue;
			if ( j == j0 ) j0 = 0;
			/* set j-th bit */
			fd[j>>5] |= (1<<(j&31));
 			for ( k = 1; k < j; k++ ) {
				/* skip the polynomials 'before' f */
				if ( k < k0 ) continue;
				else if ( k == k0 ) { k0 = 0; continue; }
				/* set k-th bit */
				fd[k>>5] |= (1<<(k&31));
				ret = irredcheck_dddup2(f);
				if ( ret == 1 ) return 0;
				/* reset k-th bit */
				fd[k>>5] &= ~(1<<(k&31));
			}
			/* reset j-th bit */
			fd[j>>5] &= ~(1<<(j&31));
		}
		/* reset i-th bit */
		fd[i>>5] &= ~(1<<(i&31));
	}
	/* exhausted */
	return 1;
}

printqmat(mat,row,col)
Q **mat;
int row,col;
{
	int i,j;

	for ( i = 0; i < row; i++ ) {
		for ( j = 0; j < col; j++ ) {
			printnum(mat[i][j]); printf(" ");
		}
		printf("\n");
	}
}

printimat(mat,row,col)
int **mat;
int row,col;
{
	int i,j;

	for ( i = 0; i < row; i++ ) {
		for ( j = 0; j < col; j++ ) {
			printf("%d ",mat[i][j]);
		}
		printf("\n");
	}
}