/* List of functions:
   getMaxRootOfUnityToAddToCharZeroPID(R)
   getA4CopyGroups(R)
   getS4CopyGroups(R)
   getConjCopyDesc(numCopyLst,n)
   getFiniteInsolubleMatrixGroupsOfDimThreeOverPIDOfCharZero(R, isGL, isByConj)
   getFiniteMatrixGroupsOfDimThreeOverPIDOfCharZero(R, isGL, isByConj)
**************************/

getMaxRootOfUnityToAddToCharZeroPID:= function(R)

	if (Category(R) eq RngUPol) then
		while (Category(R) eq RngUPol) do // find ground ring of univariate polynomial ring
			R:= BaseRing(R);  
		end while;		
	end if;

	if (Category(R) in [FldRe, FldRat, RngInt]) then return 2; end if;
	if (Category(R) eq FldCom) then return 3; end if;	
	if (Category(R) eq FldNum) then R:=AbsoluteField(R); end if;

	K:= Integers(R);
	u:= UnitGroup(K).1;
	return Order(u);

end function;

getA4CopyGroups:= function(R)
	local matA, matB1, matB2;
	local MG1, MG2;

	matA:= GL(3,R)! Matrix([[0,1,0],
							[0,0,1],
							[1,0,0]]);
	matB1:= GL(3,R)! Matrix([[ 0, 0, 1],
							 [-1,-1,-1],
							 [ 1, 0, 0]]);
	matB2:= GL(3,R)! Matrix([[-1,0,0],
							 [-1,0,1],
							 [-1,1,0]]);
							 
	MG1:= MatrixGroup<3, R | matA, matB1>;
	MG2:= MatrixGroup<3, R | matA, matB2>;
	
	return MG1, MG2;
end function;

getS4CopyGroups:= function(R)
	local matA, matB1, matB2;
	local MG1, MG2;

	matA:= GL(3,R)! Matrix([[0,1,0],
							[0,0,1],
							[1,0,0]]);
	matB1:= GL(3,R)! Matrix([[ 1, 1,1],
							 [-1, 0,0],
							 [ 0,-1,0]]);
	matB2:= GL(3,R)! Matrix([[ 0, 1,-1],
							 [-1, 1, 0],
							 [ 0, 1, 0]]);
							 
	MG1:= MatrixGroup<3, R | matA, matB1>;
	MG2:= MatrixGroup<3, R | matA, matB2>;
	
	return MG1, MG2;
end function;

getConjCopyDesc:= function(numCopyLst,n)
	if IsEven(n) then
		if (numCopyLst eq 1) then 
			return " (NonConj Copy)";
		else
			return " (2nd NonConj Copy)";
		end if;
	else
		if (numCopyLst eq 1) then 
			if (n gt 0) then return " (2nd NonConj Copy)"; 
			else
				return " (3rd NonConj Copy)";
			end if;
		else
			if (n gt 0) then return " (4th NonConj Copy)"; 
			else
				return " (5th NonConj Copy)";
			end if;
		end if;
	end if;
end function;

getFiniteSolubleMatrixGroupsOfDimThreeOverPIDOfCharZero:= function(R, isGL, isByConj)
	//c.f. Theorem 7.3.8, Corollary 7.3.9.
	// ***Declarations***************************************** //

	local intMaxRu;
	local strSLExtraCond;
	local two;

	// Lists
	local lst_alt4, lst_sym4;
	local lst_alt4copies, lst_sym4copies;
	local lst_alt4_2ndcopies, lst_sym4_2ndcopies, lst_sym4_3rdcopies;

	// Matrix Groups
	local MG_Alt4, MG_Alt4C1, MG_Alt4C2;
	local MG_Sym4, MG_Sym4C, MG_Sym4C1, MG_Sym4C2;

	// maps
	local ru, DM;
	
	// ******************************************************* //

	if (Category(R) eq FldCom) and (isGL) then
		return "Error: Cannot compute all soluble insoluble irreducible finite matrix subgroups of GL(3,C) as list is infinite. \n You can compute for SL(3,C) if you like.";
	end if;
	
	intMaxRu := getMaxRootOfUnityToAddToCharZeroPID(R);
	ru:= getRootOfUnityMap(R, intMaxRu);

	DM:= map<Integers() -> GL(3,R) | r:-> GL(3,R)! DiagonalMatrix([ru(r),ru(r),ru(r)])>;
	DMv:= map<Integers() -> GL(3,R) | r:-> GL(3,R)! DiagonalMatrix([r,r,r])>;
	
	if (isGL) then
		strSLExtraCond := "true";
	else
		strSLExtraCond := "ru(r)^3 eq 1";
	end if;
		
	MG_Alt4:= getAlt4AsMG(R);
	MG_Sym4:= getSym4AsMG(R);

	lst_alt4 := [makeMG(R, [(GL(3,R)! MG_Alt4.1), DM(r)*(GL(3,R)! MG_Alt4.2)],
				<"A4", "A4" cat getCyclicDesc(r), "(i)", MG_Alt4`Properties>) : r in Divisors(intMaxRu) | eval(strSLExtraCond)];
	
	lst_sym4 := [makeMG(R, [DM(r)*(GL(3,R)! MG_Sym4.1), (GL(3,R)! MG_Sym4.2)],
				<"S4", "S4" cat getCyclicDesc(r), "(ii)", MG_Sym4`Properties>) : r in Divisors(intMaxRu) | eval(strSLExtraCond)];
	
	if (isByConj) then
		MG_Sym4C := getSym4NonConjCopyAsMG(R); 
		lst_sym4copies := [makeMG(R, [DM(r)*(GL(3,R)! MG_Sym4C.1), (GL(3,R)! MG_Sym4C.2)],
						  <"S4", "S4" cat getCyclicDesc(r) cat " (NonConj Copy)", "(ii)", MG_Sym4`Properties>) : r in Divisors(intMaxRu) | Gcd(2,r) eq 1 and eval(strSLExtraCond)];
		lst_sym4 := lst_sym4 cat lst_sym4copies;
	end if;
		
	two:= R! 2;
	
	if not(IsUnit(two)) and (isByConj) then
		MG_Alt4C1, MG_Alt4C2 := getA4CopyGroups(R);
		lst_alt4copies := [makeMG(R, [(GL(3,R)! MG_Alt4C1.1), DM(r)*(GL(3,R)! MG_Alt4C1.2)],
			<"A4", "A4" cat getCyclicDesc(r) cat " (NonConj Copy)", "(i)", MG_Alt4`Properties>) : r in Divisors(intMaxRu) | eval(strSLExtraCond)];
		lst_alt4_2ndcopies := [makeMG(R, [(GL(3,R)! MG_Alt4C2.1), DM(r)*(GL(3,R)! MG_Alt4C2.2)],
			<"A4", "A4" cat getCyclicDesc(r) cat " (2nd NonConj Copy)", "(i)", MG_Alt4`Properties>) : r in Divisors(intMaxRu) | eval(strSLExtraCond)];
		lst_alt4 := lst_alt4 cat lst_alt4copies cat lst_alt4_2ndcopies;
		
		MG_Sym4C1, MG_Sym4C2 := getS4CopyGroups(R);
		lst_sym4_2ndcopies := [makeMG(R, [DM(r)*(GL(3,R)! MG_Sym4C1.1), (GL(3,R)! (DMv(d)*MG_Sym4C1.2))],
			<"S4", "S4" cat getCyclicDesc(r) cat getConjCopyDesc(1,r*d), "(ii)", MG_Sym4`Properties>) : r in Divisors(intMaxRu), d in [1,-1] | d in [1,((-1)^Gcd(2,r))] and eval(strSLExtraCond)];
		lst_sym4_3rdcopies := [makeMG(R, [DM(r)*(GL(3,R)! MG_Sym4C2.1), (GL(3,R)! (DMv(d)*MG_Sym4C2.2))],
			<"S4", "S4" cat getCyclicDesc(r) cat getConjCopyDesc(2,r*d), "(ii)", MG_Sym4`Properties>) : r in Divisors(intMaxRu), d in [1,-1] | d in [1,((-1)^Gcd(2,r))] and eval(strSLExtraCond)];
		lst_sym4:= lst_sym4 cat lst_sym4_2ndcopies cat lst_sym4_3rdcopies;
	end if;

	return [lst_alt4] cat [lst_sym4];

end function;

getFiniteInsolubleMatrixGroupsOfDimThreeOverPIDOfCharZero:= function(R, isGL, isByConj)
	//c.f. Theorems 7.3.8.
	// ***Declarations***************************************** //

	local P, x;
	local intMaxRu;
	local strSLExtraCond;
	
	// Booleans
	local isAlt5, is3Alt6, isPSL27;

	// Lists
	local lst_alt5, lst_3alt6, lst_psl27;
	local lst_alt5xCr, lst_3alt6xCr, lst_3alt6oC9r, lst_psl27xCr;

	// Matrix Groups
	local MG_Alt5, MG_3Alt6, MG_PSL27;

	// maps
	local ru, DM;
	
	// ******************************************************* //

	if (Category(R) eq FldCom) and (isGL) then
		return "Error: Cannot compute all insoluble irreducible finite matrix subgroups of GL(3,C) as list is infinite. \n You can compute for SL(3,C) if you like.";
	end if;
	
	intMaxRu := getMaxRootOfUnityToAddToCharZeroPID(R);
	ru:= getRootOfUnityMap(R, intMaxRu);	

	DM:= map<Integers() -> GL(3,R) | r:-> GL(3,R)! DiagonalMatrix([ru(r),ru(r),ru(r)])>;

	P<x>:= PolynomialRing(R);
	lst_alt5:= [];
	lst_3alt6:= [];
	lst_psl27:= [];

	if (isGL) then
		strSLExtraCond := "true";
	else
		strSLExtraCond := "ru(r)^3 eq 1";
	end if;

	isAlt5, MG_Alt5 := getAlt5AsMG(R);
	if (isAlt5) then
		lst_alt5xCr:= [makeMG(R, [DM(r)*(GL(3,R)! MG_Alt5.1), GL(3,R)! MG_Alt5.2], <"A5", "A5 x C_" cat IntegerToString(r), "", MG_Alt5`Properties>) :
				       r in Divisors(intMaxRu) | r ne 1 and eval(strSLExtraCond)];
		lst_alt5:= [MG_Alt5] cat lst_alt5xCr;
	end if;

	is3Alt6, MG_3Alt6 := get3Alt6AsMG(R);
	if (is3Alt6) then
		lst_3alt6xCr:= [makeMG(R, [DM(r)*(GL(3,R)! MG_3Alt6.1), GL(3,R)! MG_3Alt6.2], <"A6", "3.A6 x C_" cat IntegerToString(r), "", MG_3Alt6`Properties>) :
			r in Divisors(intMaxRu) | r mod 3 ne 0 and r ne 1 and eval(strSLExtraCond)];
		if (intMaxRu mod 9 eq 0) and (isGL) then
			lst_3alt6oC9r := [makeMG(R, [DM(9*r)*MG_3Alt6.1, MG_3Alt6.2], <"A6", "3.A6 x C_" cat IntegerToString(9*r), "">) :
				r in Divisors(intMaxRu) | r in Divisors(intMaxRu div 9)];
		else
			lst_3alt6oC9r := [];
		end if;
		lst_3alt6:= [MG_3Alt6] cat lst_3alt6xCr cat lst_3alt6oC9r;	
	end if;

	isPSL27, MG_PSL27 := getPSL27AsMG(R);
	if (isPSL27) then
		lst_psl27xCr:= [makeMG(R, [GL(3,R)! MG_PSL27.1, DM(r)*(GL(3,R)! MG_PSL27.2)], <"PSL(2,7)", "L_2(7) x C_" cat IntegerToString(r), "", MG_PSL27`Properties>) :
				r in Divisors(intMaxRu) | r ne 1 and eval(strSLExtraCond)];
		lst_psl27:= [MG_PSL27] cat lst_psl27xCr;
	end if;

	return [lst_alt5] cat [lst_3alt6] cat [lst_psl27];

end function;

getFiniteMatrixGroupsOfDimThreeOverPIDOfCharZero:= function(R, isGL, isByConj)

	if (Category(R) eq FldCom) and (isGL) then
		return "Error: Cannot compute all finite irreducible matrix groups of GL(3,C) as list is infinite. \n You can compute for SL(3,C) if you like.";
	end if;
	
	return getFiniteSolubleMatrixGroupsOfDimThreeOverPIDOfCharZero(R, isGL, isByConj) cat
	       getFiniteInsolubleMatrixGroupsOfDimThreeOverPIDOfCharZero(R, isGL, isByConj);
	       
end function;