load "attributes.m";
load "common.m";
load "misc.m";
load "deg2_abelian.m";
load "deg2_insoluble.m";
load "deg2_monomial.m";
load "deg2_primitive.m";
load "deg2_char_zero.m";
load "deg3_groups.m";
load "deg3_insoluble.m";
load "deg3_char_zero.m";
load "deg3_monomial.m";
load "deg3_primitive.m";

/* List of functions:
	getMatrixSubgroupsOverDegreeTwo(f,L)
	getMatrixSubgroupsOverDegreeThree(f,L)
	getMatrixSubgroupsOverSmallDegree(d, fF, L)
	getMatSubgroupsSD(d, fF, L)
	getInsolubleMatSubgroupsSD(d, fF)
**************************/

getMatrixSubgroupsOverDegreeTwo:= function (f,L)

	local isByConj, isGL;

	if ("by_iso" in L) then
		isByConj:= false;
	else
		isByConj:= true;
	end if;

	if ("SL" in L) and not("GL" in L) then
		isGL:= false;
	else
		isGL:= true;
	end if;

	if Category(f) eq RngIntElt then
		try
			f:= GF(f);  // make into field
		catch e
			return "Error: Prime power expected for field.";
		end try;
	end if;
	
	try
		if not(Category(f) in [RngOrd, RngQuad]) and not(IsPID(f)) then
			return "Error: PID or prime power expected.";
		end if;
	catch e
		return "Error: PID or prime power expected.";
	end try;

	if (Characteristic(f) eq 0) then
		if ("insoluble" in L) and not("soluble" in L) then
			return getFiniteInsolubleMatrixGroupsOfDimTwoOverPIDOfCharZero(f, isGL, isByConj);
		elif ("soluble" in L) and not("insoluble" in L) then
			return getFiniteSolubleMatrixGroupsOfDimTwoOverPIDOfCharZero(f, isGL, isByConj);
		else
			return getFiniteMatrixGroupsOfDimTwoOverPIDOfCharZero(f, isGL, isByConj);
		end if;
	else
		flag, p, a := IsPrimePower(#f);
		lstMat := [];
		
		if ("monomial" in L) or ("soluble" in L) or (not("insoluble" in L) and not("primitive" in L)) then
			Append(~lstMat, Degree2MonomialList(p^a));
		end if;

		if ("primitive" in L) or ("soluble" in L) or (not("insoluble" in L) and not("monomial" in L)) then
			Append(~lstMat, Degree2PrimitiveList(p^a));
		end if;
		
		if ("soluble" in L) or (not("insoluble" in L) and not("monomial" in L) and not("primitive" in L)) then
			lstMat := lstMat cat [AbelianList(2, p^a)] cat [SingerCycleList(2, p^a)];
		end if;

		if ("insoluble" in L) or (not("soluble" in L) and not("primitive" in L) and not("monomial" in L)) then
			Append(~lstMat, Degree2InsolubleList(p^a));
		end if;

		return lstMat;
	end if;
	
end function;

getMatrixSubgroupsOverDegreeThree:= function (f,L)

	local isByConj, isGL;
	local flag, p, a;

	if ("by_iso" in L) then
		isByConj:= false;
	else
		isByConj:= true;
	end if;

	if ("SL" in L) and not("GL" in L) then
		isGL:= false;
	else
		isGL:= true;
	end if;

	if Category(f) eq RngIntElt then
		try
			f:= GF(f);  // make into field
		catch e
			return "Error: Prime power expected for field.";
		end try;
	end if;

	try
		if not(Category(f) in [RngOrd, RngQuad]) and not(IsPID(f)) then
			return "Error: PID or prime power expected.";
		end if;
	catch e
		return "Error: PID or prime power expected.";
	end try;

	if (Characteristic(f) eq 0) then
		if ("insoluble" in L) and not("soluble" in L) then
			return getFiniteInsolubleMatrixGroupsOfDimThreeOverPIDOfCharZero(f, isGL, isByConj);
		elif ("soluble" in L) and not("insoluble" in L) then
			return getFiniteSolubleMatrixGroupsOfDimThreeOverPIDOfCharZero(f, isGL, isByConj);
		else
			return getFiniteMatrixGroupsOfDimThreeOverPIDOfCharZero(f, isGL, isByConj);
		end if;
	else	
		flag, p, a := IsPrimePower(#f);
		lstMat := [];
	
		if ("monomial" in L) or ("soluble" in L) or (not("insoluble" in L) and not("primitive" in L)) then
			lstMat := lstMat cat [MonomialList(p^a)];
		end if;

		if ("primitive" in L) or ("soluble" in L) or (not("insoluble" in L) and not("monomial" in L)) then
			lstMat := lstMat cat [PrimitiveList(p^a)];
		end if;
		
		if ("soluble" in L) or (not("insoluble" in L) and not("monomial" in L) and not("primitive" in L)) then
			lstMat := lstMat cat [AbelianList(3, p^a)] cat [SingerCycleList(3, p^a)];
		end if;
		
		if ("insoluble" in L) or (not("soluble" in L) and not("primitive" in L) and not("monomial" in L)) then
			if IsEmpty(lstMat) then
				lstMat := getInsolubleMatrixGroupsOfDimThreeFF(f, isGL, isByConj);
			else
				lstMat:= lstMat cat getInsolubleMatrixGroupsOfDimThreeFF(f, isGL, isByConj);
			end if;
		end if;

		return lstMat;
	end if;
	
end function;

getMatrixSubgroupsOverSmallDegree:= function (d, fF, L)

	// insoluble, soluble, irreducible, reducible, primitive, imprimitive

	matList := [];

	if (Category(d) eq RngIntElt) then

		if (d eq 2) then
			if (Category(fF) eq SeqEnum) then
				
				for i in [1..#fF] do
					matList := matList cat getMatrixSubgroupsOverDegreeTwo(fF[i],L);
				end for;

			else 
				matList := getMatrixSubgroupsOverDegreeTwo(fF,L);
			end if;
			
			if (Category(matList) eq SeqEnum) then 
				return &cat(matList);
			else
				return matList;
			end if;
		end if;
		
		if (d eq 3) then // Degree 3 Case
			if (Category(fF) eq SeqEnum) then
				
				for i in [1..#fF] do
					matList := matList cat getMatrixSubgroupsOverDegreeThree(fF[i],L);
				end for;

			else 
				matList := getMatrixSubgroupsOverDegreeThree(fF,L);				
			end if;

			if (Category(matList) eq SeqEnum) then 
				return &cat(matList);
			else
				return matList;
			end if;
		end if;	

		if (d eq "both") then	
			if (Category(fF) eq SeqEnum) then
				
				for i in [1..#fF] do
					matList := matList cat getMatrixSubgroupsOverDegreeTwo(fF[i],L) cat getMatrixSubgroupsOverDegreeThree(fF[i],L);
				end for;

			else 
				matList := getMatrixSubgroupsOverDegreeTwo(fF,L) cat getMatrixSubgroupsOverDegreeThree(fF,L);				
			end if;

			if (Category(matList) eq SeqEnum) then 
				return &cat(matList);
			else
				return matList;
			end if;
		end if;

		return "Error with value for degree entered";	

	end if;

	return "Some error has occurred.";

end function;

getMatSubgroupsSD := function (d, fF, L)
	return getMatrixSubgroupsOverSmallDegree(d, fF, L);
end function;

getInsolubleMatSubgroupsSD := function (d, fF)
	return getMatrixSubgroupsOverSmallDegree(d, fF, ["insoluble"]);
end function;