#include "defs.h"

extern ASCENT,GCDTIME,UFTIME,RESTIME$
extern Ord,PRINT,NOCRIB,NOSUGAR$

def sp(P)
{
	RESTIME=UFTIME=GCDTIME=SQTIME=0;
	L = flatmf(fctr(P)); X = var(P);
	AL = []; ADL = [];
	while ( 1 ) {
		L = sort_by_deg(L);
		for ( T = L, H = []; T != []; H = cons(car(T),H), T = cdr(T) )
			if ( deg(car(T),X) > 1 )
				break;
		if ( T == [] ) {
			if ( PRINT ) {
				print(["GCDTIME = ",GCDTIME]);
				print(["UFTIME = ",UFTIME]);
				print(["RESTIME = ",RESTIME]);
			}
			return [L,ADL];
		} else {
			A = newalg(car(T));
			R = pdiva(car(T),X-A);
			AL = cons(A,AL);
			ADL = cons([A,defpoly(A)],ADL);
			L = aflist(append(H,append([X-A,R],cdr(T))),AL);
		}
	}
}

def aflist(L,AL)
{
	for ( DC = []; L != []; L = cdr(L) ) {
		T = af_sp(car(L),AL,1);
		DC = append(DC,T);
	}
	return DC;
}

def sort_by_deg(F)
{
	for ( T = F, S = []; T != []; T = cdr(T) )
		if ( type(car(T)) != NUM )
			S = cons(car(T),S);
	N = length(S); W = newvect(N);
	for ( I = 0; I < N; I++ )
		W[I] = S[I];
	V = var(W[0]);
	for ( I = 0; I < N; I++ ) {
		for ( J = I + 1, J0 = I; J < N; J++ )
			if ( deg(W[J0],V) > deg(W[J],V) )
				J0 = J;
		if ( J0 != I ) {
			T = W[I]; W[I] = W[J0]; W[J0] = T;
		}
	}
	if ( ASCENT )
		for ( I = N-1, S = []; I >= 0; I-- )
			S = cons(W[I],S);
	else
		for ( I = 0, S = []; I < N; I++ )
			S = cons(W[I],S);
	return S;	
}

def flatmf(L) {
	for ( S = []; L != []; L = cdr(L) )
		if ( type(F=car(car(L))) != NUM )
			S = append(S,[F]);
	return S;
}

def af(P,AL)
{
	S = reverse(asq(P));
	for ( L = []; S != []; S = cdr(S) ) {
		FM = car(S); F = FM[0]; M = FM[1];
		G = af_sp(F,AL,1);
		for ( ; G != []; G = cdr(G) )
			L = cons([car(G),M],L);
	}
	return L;
}

def af_sp(P,AL,HINT)
{
	if ( !P || type(P) == NUM )
		return [P];
	P1 = simpalg(P);
	return af_spmain(P,AL,1,HINT);
}

def af_spmain(P,AL,INIT,HINT)
{
	if ( DEG(P) == 1 )
		return [simpalg(P)];
	if ( AL == [] ) {
/*		print(["hint = ",HINT,"P = ",P]); */
		TTT = time()[0];
		F = flatmf(ufctrhint_heuristic(P,HINT));
		UFTIME+=time()[0]-TTT;
		return F;
	}
	A0 = car(AL); P0 = defpoly(A0);
	V = var(P); V0 = var(P0);
	P = simpcoef(P);
	TTT = time()[0];
	N = simpcoef(sp_norm(A0,V,subst(P,V,V-INIT*A0),AL));
	RESTIME+=time()[0]-TTT;
	DCSQ = sortfs(asq(N));
	for ( G = P, A = V+INIT*A0, DCR = []; DCSQ != []; DCSQ = cdr(DCSQ) ) {
		C = TT(DCSQ); D = TS(DCSQ);
		if ( !var(C) )
			continue;
		if ( D == 1 )
			DCT = af_sp(C,cdr(AL),HINT*deg(P0,V0));
		else
			DCT = af_sp(C,cdr(AL),1);
		for ( ; DCT != []; DCT = cdr(DCT) ) {
			if ( !var(car(DCT)) )
				continue;
			if ( length(DCSQ) == 1 && length(DCT) == 1 )
				U = simpcoef(G);
			else {
				S = subst(car(DCT),V,A);
				L = pqra(G,S);
				if ( L[1] )
					U = gcda(S,G);
				else
					U = S;
			}
			if ( var(U) == V ) {
				G = pdiva(G,U);
				if ( D == 1 )
					DCR = cons(simpcoef(U),DCR);
				else {
					T = af_spmain(U,AL,sp_next(INIT),HINT);
					DCR = append(DCR,T);
				}
			}
		}
	}
	return DCR;
}

def sp_next(I)
{
	if ( I > 0 )
		return -I;
	else
		return -I+1;
}

#if 1
def sp_norm(A,V,P,AL)
{
	Len = length(AL);	
	P0 = defpoly(A); V0 = var(P0);
	PR = algptorat(P);
	if ( nmono(P0) == 2 )
		R = res(V0,PR,P0);
	else if ( Len == 1 || Len == 3 )
		R = res_ch1(V0,V,PR,P0);
	else if ( Len == 2 ) {
		P1 = defpoly(AL[1]);
		R = norm_ch1(V0,V,PR,P0,P1);
	} else
		R = res(V0,PR,P0);
	return rattoalgp(R,cdr(AL));
}
#endif

#if 0
def sp_norm(A,V,P,AL)
{
	Len = length(AL);	
	P0 = defpoly(A); V0 = var(P0);
	PR = algptorat(P);
	R = res(V0,PR,P0);
	return rattoalgp(R,cdr(AL));
}
#endif

def simpalg(P) {
	if ( !P )
		return 0;
	else if ( type(P) == NUM )
		return ntype(P) <= 1 ? P : simpalgn(P);
	else if ( type(P) == POLY )
		return simpalgp(P);
	else if ( type(P) == RAT )
		return simpalg(nm(P))/simpalg(dn(P));
}

def simpalgp(P) {
	for ( V = var(P), I = deg(P,V), T = 0; I >= 0; I-- )
		if ( C = coef(P,I) )
			T += simpalg(C)*V^I;
	return T;
}
		
def simpalgn(A) {
	if ( ntype(A) <= 1 )
		return A;
	else if ( type(R=algtorat(A)) == POLY )
		return simpalgb(A);
	else
		return simpalgb(
			invalgp(simpalgb(rattoalg(dn(R))))
			*simpalgb(rattoalg(nm(R)))
		);
}

def simpalgb(P) {
	if ( ntype(P) <= 1 )
		return P;
	else {
		for ( A = getalg(P), S = algtorat(P); 
			A != []; A = cdr(A) )
			S = srem(S,defpoly(car(A)));
		return rattoalg(S);
	}
}

def getalgp(P) {
	if ( type(P) <= 1 )
		return getalg(P);
	else {
		for ( V = var(P), I = deg(P,V), T = []; I >= 0; I-- )
			if ( C = coef(P,I) )
				T = union(T,getalgp(C));
		return T;
	}
}
		
def union(A,B)
{
	for ( T = B; T != []; T = cdr(T) )
		A = union1(A,car(T));
	return A;
}

def union1(A,E)
{
	if ( A == [] )
		return [E];
	else if ( car(A) == E )
		return A;
	else
		return cons(car(A),union1(cdr(A),E));
}

def invalgp(A)
{
	if ( ntype(A) <= 1 )
		return 1/A;
	P0 = defpoly(mainalg(A)); P = algtorat(A);
	V = var(P0); G1 = P0;
	G2 = DEG(P)>=DEG(P0)?srem(P,P0):P;
	for ( H = 1, X = 1, U1 = 0, U2 = 1; deg(G2,V); ) {
		D = DEG(G1)-DEG(G2); T = LCOEF(G2)^(D+1);
		L = sqr(G1*T,G2); Q = car(L); R = car(cdr(L));
		S = U1*T-U2*Q;
		M = H^D; M1 = M*X;
		G1 = G2; G2 = sdiv(R,M1);
		U1 = U2; U2 = sdiv(S,M1);
		X = LCOEF(G1); H = sdiv(X^D*H,M);
	}
	C = invalgp(rattoalg(srem(P*U2,P0)));
	return C*rattoalg(U2);
}

def algptorat(P) {
	if ( type(P) <= 1 )
		return algtorat(P);
	else {
		for ( V = var(P), I = deg(P,V), T = 0; I >= 0; I-- )
			if ( C = coef(P,I) )
				T += algptorat(C)*V^I;
		return T;
	}
}

def rattoalgp(P,M) {
	for ( T = M, S = P; T != []; T = cdr(T) )
		S = subst(S,algtorat(FIRST(T)),FIRST(T));
	return S;
}
def sortfs(L)
{
#define Factor(a) car(a)
#define Mult(a) car(cdr(a))
	if ( type(TT(L)) == NUM )
		L = cdr(L);
	for ( N = 0, T = L; T != []; T = cdr(T), N++ );	
	P = newvect(N); P1 = newvect(N);
	for ( I = 0, T = L, R = []; T != []; T = cdr(T) )
		if ( Mult(car(T)) == 1 ) {
			R = cons(car(T),R); N--;
		} else {
			P[I] = car(T); I++;
		}
	for ( J = 0, V = var(Factor(P[0])); J < N; J++ ) {
		for ( K0 = K = J, D = deg(Factor(P[J]),V); K < N; K++ )
			if ( deg(Factor(P[K]),V) < D ) {
				K0 = K;
				D = deg(Factor(P[K]),V);
			}
			P1[J] = P[K0];
			if ( J != K0 )
				P[K0] = P[J];
	}
	for ( I = N - 1; I >= 0; I-- )
		R = cons(P1[I],R);
	return R;
}

def pdiva(P1,P2)
{
	A = union(getalgp(P1),getalgp(P2));
	P1 = algptorat(P1); P2 = algptorat(P2);
	return simpalg(rattoalgp(sdiv(P1*LCOEF(P2)^(DEG(P1)-DEG(P2)+1),P2),A));
}

def pqra(P1,P2)
{
	if ( type(P2) != POLY )
		return [P1,0];
	else if ( (type(P1) != POLY) || (deg(P1,var(P1)) < deg(P2,var(P1))) )
		return [0,P1];
	else {
		A = union(getalgp(P1),getalgp(P2));
		P1 = algptorat(P1); P2 = algptorat(P2);
		L = sqr(P1*LCOEF(P2)^(DEG(P1)-DEG(P2)+1),P2);
		return [simpalg(rattoalgp(L[0],A)),simpalg(rattoalgp(L[1],A))];
	}
}

def gcda(P1,P2)
{
	if ( !var(P1) || !var(P2) )
		return 1;
	AL = union(getalgp(P1),getalgp(P2));
	if ( AL == [] )
		return gcd(P1,P2);
	G1 = algptorat(P1); G2 = algptorat(P2);
	for ( ML = [G1,G2],VL = [], T = AL; T != []; T = cdr(T) ) {
		VL = cons(algptorat(car(T)),VL);
		ML = cons(defpoly(car(T)),ML);
	}
	VL = sort_alg(VL);
	T = time()[0];
	PRINT = 0; Ord = 2; NOCRIB = 0; NOSUGAR = 1;
	L = grm(ML,cons(var(P1),VL));
	GCDTIME += time()[0]-T;
	for ( ; L != []; L = cdr(L) ) {
		S = rattoalgp(car(L),AL);
		if ( type(S) != NUM )
			return S;
	}
	return 1;
}

def sort_alg(VL)
{
	N = length(VL); W = newvect(N,VL);
	for ( I = 0; I < N; I++ ) {
		for ( M = I, J = I + 1; J < N; J++ )
			if ( W[J] > W[M] )
				M = J;
		if ( I != M ) {
			T = W[I]; W[I] = W[M]; W[M] = T;
		}
	}
	for ( I = N-1, L = []; I >= 0; I-- )
		L = cons(W[I],L);
	return L;
}

def asq(P)
{
	P = simpalg(P);
	if ( type(P) == NUM )
		return [[1,1]];
	else if ( getalgp(P) == [] )
		return sqfr(P);
	else {
		V = var(P); N = DEG(P); A = newvect(N+1); B = newvect(N+1);
		for ( I = 0, F = P; ;I++ ) {
			if ( type(F) == NUM ) 
				break;
			F1 = diff(F,V);
			GCD = gcda(F,F1);
			FLAT = pdiva(F,GCD);
			if ( type(GCD) == NUM ) {
				A[I] = F; B[I] = 1; 
				break;
			}
			for ( J = 1, F = GCD; ; J++ ) {
				L = pqra(F,FLAT); Q = L[0]; R = L[1];
				if ( R )
					break;
				else
					F = Q;
			}
			A[I] = FLAT; B[I] = J;
		}
		for ( I = 0, J = 0, L = []; A[I]; I++ ) {
			J += B[I];
			if ( A[I+1] )
				C = pdiva(A[I],A[I+1]);
			else
				C = A[I];
			L = cons([C,J],L);
		}
		return L;
	}
}

def ufctrhint1(P,HINT)
{
	if ( deg(P,var(P)) == 168 ) {
		SQ = sqfr(P);
		if ( length(SQ) == 2 && SQ[1][1] == 1 )
			return [[1,1],[P,1]];
		else
			return ufctrhint(P,HINT);
	} else
		return ufctrhint(P,HINT);
}	

def simpcoef(P) {
	return rattoalgp(ptozp(algptorat(P)),getalgp(P));
}

def ufctrhint_heuristic(P,HINT) {
	V = var(P); D = deg(P,V);
	if ( D == HINT )
		return [[P,1]];
	for ( T = P-coef(P,D)*V^D, G = D; T; T -= coef(T,DT)*V^DT )
		G = igcd(G,DT=deg(T,V));
	if ( G == 1 )
		return ufctrhint(P,HINT);
	else {
		for ( S = 0, T = P; T; T -= coef(T,DT)*V^DT ) {
			DT = deg(T,V);
			S += coef(T,DT)*V^(DT/G);
		}
		L = fctr(S);
		for ( DC = [car(L)], L = cdr(L); L != []; L = cdr(L) ) {
			T = ufctrhint(subst(car(car(L)),V,V^G),HINT);
			DC = append(DC,cdr(T));
		}
		return DC;
	}
}	

def res_det(V,P1,P2)
{
	D1 = deg(P1,V); D2 = deg(P2,V);
	M = newmat(D1+D2,D1+D2);
	for ( J = 0; J <= D2; J++ )
		M[0][J] = coef(P2,D2-J,V);
	for ( I = 1; I < D1; I++ )
		for ( J = 0; J <= D2; J++ )
		M[I][I+J] = M[0][J];
	for ( J = 0; J <= D1; J++ )
		M[D1][J] = coef(P1,D1-J,V);
	for ( I = 1; I < D2; I++ )
		for ( J = 0; J <= D1; J++ )
		M[D1+I][I+J] = M[D1][J];
	return det(M);
}

def norm_ch1(V0,VM,P,P0,PR) {
	D = deg(P,V0); D0 = deg(P0,V0); DM = deg(P,VM); N = DM*D0;
	X = newvect(N+1); V = newvect(N+1); U = newvect(N+1);
	Min = -idiv(N,2);	
	C = coef(P,D,V0);
	for ( I = J = 0; I <= N; J++ ) {
		T=J+Min;
		if ( subst(C,VM,T) ) {
			U[I] = srem(res(V0,subst(P,VM,T),P0),PR);
			X[I++] = T;
		}
	}
	for ( I = 1, M = 1, S = V[0] = U[0]; I <= N; I++ ) {
		for ( J = 0, T = U[I]; J < I; J++ )
			T = sdiv(T-V[J],X[I]-X[J]);
		V[I] = T;
		M *= (VM-X[I-1]);
		S += T*M;
	}
	return S;
}

def norm_ch2(V0,VM,P,P0,PR) {
	D = deg(P,V0); D0 = deg(P0,V0); DM = deg(P,VM); N = DM*D0;
	X = newvect(N+1); V = newvect(N+1); U = newvect(N+1);
	Min = -idiv(N,2);	
	C = coef(P,D,V0);
	for ( I = J = 0; I <= N; J++ ) {
		T=J+Min;
		if ( subst(C,VM,T) ) {
			U[I] = srem(res_det(V0,subst(P,VM,T),P0),PR);
			X[I++] = T;
		}
	}
	for ( I = 1, M = 1, S = V[0] = U[0]; I <= N; I++ ) {
		for ( J = 0, T = U[I]; J < I; J++ )
			T = sdiv(T-V[J],X[I]-X[J]);
		V[I] = T;
		M *= (VM-X[I-1]);
		S += T*M;
	}
	return S;
}

def res_ch1(V0,VM,P,P0) {
	D = deg(P,V0); D0 = deg(P0,V0); N = deg(P,VM)*D0+deg(P0,VM)*D;
	X = newvect(N+1); V = newvect(N+1); U = newvect(N+1);
	Min = -idiv(N,2);	
	C = coef(P,D,V0); C0 = coef(P0,D0,V0);
	for ( I = J = 0; I <= N; J++ ) {
		T=J+Min;
		if ( subst(C,VM,T) && subst(C0,VM,T) ) {
			U[I] = res(V0,subst(P,VM,T),subst(P0,VM,T));
			X[I++] = T;
		}
	}
	for ( I = 1, M = 1, S = V[0] = U[0]; I <= N; I++ ) {
		for ( J = 0, T = U[I]; J < I; J++ )
			T = sdiv(T-V[J],X[I]-X[J]);
		V[I] = T;
		M *= (VM-X[I-1]);
		S += T*M;
	}
	return S;
}

def res_ch(V0,VM,P,P0) {
	D = deg(P,V0); D0 = deg(P0,V0); N = deg(P,VM)*D0+deg(P0,VM)*D;
	X = newvect(N+1); V = newvect(N+1); U = newvect(N+1);
	Min = -idiv(N,2);	
	C = coef(P,D,V0); C0 = coef(P0,D0,V0);
	for ( I = J = 0; I <= N; J++ ) {
		T=J+Min;
		if ( subst(C,VM,T) && subst(C0,VM,T) ) {
			U[I] = res_det(V0,subst(P,VM,T),subst(P0,VM,T));
			X[I++] = T;
		}
	}
	for ( I = 1, M = 1, S = V[0] = U[0]; I <= N; I++ ) {
		for ( J = 0, T = U[I]; J < I; J++ )
			T = sdiv(T-V[J],X[I]-X[J]);
		V[I] = T;
		M *= (VM-X[I-1]);
		S += T*M;
	}
	return S;
}

def norm_ch2_lag(V,VM,P,P0,PR) {
	D0 = deg(P0,V); DM = deg(P,VM); N = DM*D0;
	Min = -idiv(N,2);	
	for ( A = 1, I = 0; I <= N; I++ )
		A *= (VM-I-Min);
	for ( I = 0, S = 0; I <= N; I++ ) {
		R = res_det(V,subst(P,VM,I+Min),P0);
		R = srem(R,PR);
		T = sdiv(A,VM-I-Min);
		S += R*T/subst(T,VM,I+Min);
	}
	return S;
}

def norm_ch_lag(V,VM,P,P0) {
	D0 = deg(P0,V); DM = deg(P,VM); N = DM*D0;
	Min = -idiv(N,2);	
	for ( A = 1, I = 0; I <= N; I++ )
		A *= (VM-I-Min);
	for ( I = 0, S = 0; I <= N; I++ ) {
		R = res_det(V,subst(P,VM,I+Min),P0);
		T = sdiv(A,VM-I-Min);
		S += R*T/subst(T,VM,I+Min);
	}
	return S;
}
end$
