# Compute a "generating" set, or compute all subfields.
#
# This implementation is meant to be an illustration of the idea and
# is not optimized for speed.
#
# Mark van Hoeij,   August 2004.


# B = basefield
# f is irreducible in B[x]
#
# The output is of the form
#
#   [E1,E2,..,Ek], vars, g
#
# such that gi := subs(Ei, g) is a generic element
# of some subfield Li of K := B[x]/(f).
# What is meant by that is that:
#   [coeffs(gi, indets(gi) intersect vars)]
# is a B-vectorspace-basis of that subfield Li.
#
# These subfields L1,L2,..,Lk of K := B[x]/(f) have the
# following property: Every proper subfield of K containing B
# is an intersection of one or more Li's.
GeneratingSet := proc(f,x,B)
	local n,R,V,i,c,ansatz,vars,res,g,E;
	n := degree(f,x);
	ansatz := add(c[i]*x^i,i=0..n-1);
	vars := {seq(c[i],i=0..n-1)};
	if n<4 then
		# There can be no non-trivial subfields.
		return [],vars,ansatz
	fi;
	R := RootOf(f,x);
	# The factors of f in K[x] except x-R:
	# We also throw away any factor of degree >= n/2.
	V := FactorOverK(f,x,R,B,n);
	res := NULL;
	for g in V do
		# Get the equations. Perhaps this could be done faster?
		E := subs(x=R, ansatz) - evala(Rem(ansatz,g,x));
		E := evala({coeffs(evala(Expand(E)),{x,R})});
		# Solve the equations.
		E := SolveTools:-Linear(E,vars);
		# If subfield=B then next:
		if degree(subs(E,ansatz),x)=0 then next fi;
		res := res, E
	od;
	[res], vars, ansatz
end:


# Factor f/(x-R) over the field K.
# This is a preliminary implementation, there are tricks to compute
# this much faster.
FactorOverK := proc(f,x,R,B,n)
	local v,i;
	v := evala(Factors(numer(normal(f)), [R, op(B)]))[2];
	# Throw away polys of degree >= n/2 and polys that don't
	# contain x.
	[op( {seq(`if`(has(i,x) and degree(i[1],x) < n/2,
	 collect(i[1]/lcoeff(i[1],x),x,evala), NULL),i=v)} minus {x-R} )]
end:


# Notation the same as in GeneratingSet.
# The output is a list [L1,L2,..Ls] consisting of
# all subfields of K that contain B.
# Each Li is given in the form [b.i1,b.i2,...] where the b.ij's
# form a B-vectorspace-basis of Li.
AllSubfields := proc(f,x,B)
	local L,vars,ansatz,n,k,res,curf,p,num;
	L, vars, ansatz := GeneratingSet(args);
	n := degree(f,x);
	k := nops(L);
	res := ansatz;
	curf[0] := ansatz;
	p := 1; num[p] := -1;
	# The equations of the current field:
	curf[0] := {};
	# Find all intersections with a back-tracking search:
	while p>0 do
		if p>k or num[p]=1 then
			p:=p-1; next
		fi;
		num[p]:=num[p]+1;
		if num[p]=0 then
			curf[p] := curf[p-1];
		fi;
		if num[p]=0 and Contains(L[p], curf[p]) then
			num[p] := 1
		elif num[p]=1 then
			curf[p] := IntersectFields(L[p], curf[p], vars);
			# if curf[p] equals B or is contained in some L[i]
			# with num[i]=0 then
			if CheckCondition(num, p, L, curf[p]) then
				p := p-1; next
			fi;
			res := res, collect(subs(curf[p], ansatz),vars)
		fi;
		p := p+1;  num[p] := -1
	od;
	[seq({coeffs(p, vars intersect indets(p))}, p=[res]), {1}]
end:


# Output is true if the field L contains the field F,
# where both L and F are given in the form of equations.
Contains := proc(L, F)
	local i;
	for i in subs(F,L) do if evala(Normal(rhs(i)-lhs(i)))<>0 then
		return false
	fi od;
	true
end:


# Intersect two fields given by equations.
# Output is again given by equations.
IntersectFields := proc(L1,L2,vars)
	SolveTools:-Linear(L1 union L2, vars)
end:


CheckCondition := proc(num, p, L, F)
	local i;
	if add(`if`(rhs(i)=0,0,1),i=F)=1 then
		# now F = basefield
		return true
	fi;
	for i to p do if num[i]=0 and Contains(L[i], F) then
		# F is contained in L[i]
		return true
	fi od;
	false
end:
