############################################################
##                                                        ##
##               TorsionSubcomplex.g                      ##
## HAP subpackage for GAP (Groups Algorithms Programming) ##
##         under the GNU GPL license (v. 3),  2012        ##
##               by Alexander D. Rahm                     ##
##          IRCSET post-doctoral Research fellow          ##
##	   National University of Ireland at Galway       ##
##                                                        ##
############################################################   	

###############################################################################
# HAP subpackage by Alexander D. Rahm for extracting torsion subcomplexes     #
# from cellular complexes with group actions. Version 1.3 of June 21st, 2012. #
# In the following, whenever we talk about cell complexes, we mean a set of   #
# representatives for the action of the group,                                # 
# i.e. a strict fundamental domain.                                           #
# The syntax is TorsionSubcomplex(groupName,p);                               #
#  where p is the prime for which to extract the p-torsion subcomplex,        #
# and groupName must be a string holding between \"quotes\" an entry of the   #
# HAP library of cellular complexes with group actions,                       # 
# which admits the entries displayed with the following command.              #
# DisplayAvailableCellComplexes();					      #
# The incidence matrix of the 1-skeleton of the p-torsion subcomplex          # 
# is returned by this function. To visualize this 1-skeleton as a graph,      # 
# type instead VisualizeTorsionSkeleton(groupName,p).                         #
# If the cell complex is admissible in the sense of Brown,                    # 
# the Farrell cohomology of the group equals                                  #
# the equivariant Farrell cohomology                                          #
# of the reduced torsion subcomplex.  The syntax is                           #
# ReduceTorsionSubcomplex(groupName,p);                                       #
# and it returns us the cells which are to merge in the torsion subcomplex.   #
###############################################################################

InstallGlobalFunction("IsPnormal", function( G, p)
#########################################################################
##  Check if the group G is p-normal for the prime p.                  ##
## Zassenhaus defines a finite group to be p-normal if the center of   ##
## one of its Sylow p-groups is the center of every Sylow p-group in   ##
## which it is contained.                                              ##
#########################################################################
local SylowCenter, SylowGroups, N, isPnormal, k, j;
isPnormal := true;

  SylowGroups := ConjugateSubgroups( G, SylowSubgroup( G, p));
  N := Size( SylowGroups);

  for j in [1..N] do

	SylowCenter := Center( SylowGroups[j]);
	
	for k in [1..N] do
 	  if k <> j then

		if IsSubset(SylowGroups[k], SylowCenter) then

		  if SylowCenter = Center( SylowGroups[k]) then
			; ## the condition is fulfilled for this pair ##
		  else
			isPnormal := false;
			Print("The group ",G," is not p-normal.");
		  fi;
		fi;
	  fi;
	od;
  od;
  return isPnormal;
end);

InstallGlobalFunction("DisplayAvailableCellComplexes", function()
Exec( Concatenation("ls ",DirectoriesPackageLibrary("HAP")[1]![1], "Perturbations/Gcomplexes/")); 
end);

admissibilityCheck := function(celldata)
#########################################################
## A cell complex is admissible in the sense of Brown, ##
## if each cell stabilizer fixes its cell pointwise.   ##
## Additionally,				       ##
## we gather the cardinalities of the stabilizers.     ##
#########################################################
local stabilizerCardinalities, G, card, n, j, R, vcd, warned;
   warned := false;
   stabilizerCardinalities := [];
   vcd := Length(celldata)-1;

   for n in [0..vcd] do
	stabilizerCardinalities[n+1] := [];
	for j in [1..Length(celldata[n+1])] do
	   G :=   celldata[n+1][j]!.TheMatrixStab;
	   if IsFinite(G) then
	      card := Order(G);
	      stabilizerCardinalities[n+1][j] := card;
	      ## *** Now we have to compare              *** ##
	      ## *** with the order of "TheRotSubgroup"  *** ##	
	      R := celldata[n+1][j]!.TheRotSubgroup;
	      if card > Order(R) and warned = false then
		Print("****Warning: cell complex not admissible ",
			"in the sense of Brown!****\n",
		" Torsion subcomplex reduction requires cell subdivision.\n");
		warned := true;
	      fi;	
	   fi;
	od;
   od;
   return [stabilizerCardinalities, warned];
end;	   


computeIncidenceMatrix := function(torsionCells, numberOfTorsionCells, celldata)
########################################################
## The incidence matrix of the 1-skeleton of the      ##
## p-torsion subcomplex is returned by this function. ##
######################################################## 
local incidenceMatrix, j, k, q, endpoints, inverseIndex, ORIGIN, END;
   incidenceMatrix := [];
   for j in [1..numberOfTorsionCells[1]] do
     	incidenceMatrix[j] := [];
      	for k in [1..numberOfTorsionCells[1]] do
     		incidenceMatrix[j][k] := 0;
      	od;
   od;
   Print("The edges in the quotient by the action on the torsion subcomplex are: ");
   ## Record the indices in the torsion subcomplex of vertices in  ##
   ## the cell complex, in order to assign the endpoints of an edge##
   inverseIndex := [];
   for q in [1..Length(celldata[1])] do
	inverseIndex[q] := "error: not a p-torsion vertex";
   od;
   for j in [1..numberOfTorsionCells[1]] do
	inverseIndex[torsionCells[1][j][2]] := j;
   od;
   for q in [1..numberOfTorsionCells[2]] do
	endpoints := celldata[2]
			[torsionCells[2][q][2]]!.BoundaryImage.ListIFace;
	ORIGIN := inverseIndex[endpoints[1]];
	END    := inverseIndex[endpoints[2]];
	Print([ORIGIN,END]);
	incidenceMatrix[ORIGIN][END] := incidenceMatrix[ORIGIN][END] +1;
	## If we do not want to have the incidence matrix symmetric, ##
	## then deactivate the following line. ##
	incidenceMatrix[END][ORIGIN] := incidenceMatrix[END][ORIGIN] +1;
   od;
   Print(".\n");
   return incidenceMatrix;
end;


getCardinals := function(stabilizerCardinalities, p, n)
############################################################
## Extract the cardinalities of n-cell stabilisers        ##
## containing p-torsion.                                  ##
############################################################
local S, m, Cardinals, counter;
    Cardinals := []; counter := 0;
    S := stabilizerCardinalities[n+1]; 
    for m in S do
	if m mod p = 0 then
	  if counter > 0 then
	    if m in Cardinals then ;
	    else
		  counter := counter +1;
		  Cardinals[counter] := m;
	    fi;
	  else counter := 1;
	       Cardinals[counter] := m;
	  fi;
	fi;
    od;
    return Cardinals;
end;


extractPmultipleTorsionCells := function(torsionCells, numberOfTorsionCells, celldata,stabilizerCardinalities, p)
##################################################################
## Make lists of n-cells of each stabilizer cardinality         ##
## in the p-torsion subcomplex.                                 ##
##################################################################
local vcd, pMultipleTorsionCells, numberOfPmultipleTorsionCells, Pmultiples, PmultipleNumero, m, n, cell, j;

  vcd := Length(celldata) -1;
  pMultipleTorsionCells := [];
  numberOfPmultipleTorsionCells := [];
  Pmultiples := [];

  for n in [0..vcd] do

    pMultipleTorsionCells[n+1] := [];
    numberOfPmultipleTorsionCells[n+1] := [];
    Pmultiples[n+1] := getCardinals(stabilizerCardinalities, p, n);
    PmultipleNumero := 0;

    for m in Pmultiples[n+1] do

      PmultipleNumero := PmultipleNumero +1;
      numberOfPmultipleTorsionCells[n+1][PmultipleNumero] := 0;
      pMultipleTorsionCells[n+1][PmultipleNumero] := [];

      for cell in torsionCells[n+1] do

 	## Check if the stabilizer contains p-torsion of multiple m ##


	if stabilizerCardinalities[n+1][cell[2]] = m then	
	 
	  numberOfPmultipleTorsionCells[n+1] :=	
		numberOfPmultipleTorsionCells[n+1] +1;
 	  pMultipleTorsionCells[n+1][PmultipleNumero]
		[numberOfPmultipleTorsionCells[n+1][PmultipleNumero]] := 
		cell;
	fi;
      od;
      Print("There are ",numberOfPmultipleTorsionCells[n+1][PmultipleNumero],
		" orbits of ",n,"-cells in the ",p,"-torsion subcomplex",
		", the stabilizers of which are of cardinality ",m,
		", namely the ones numero "
	);
      for j in [1..Length(pMultipleTorsionCells[n+1][PmultipleNumero])-1] do
	    Print(pMultipleTorsionCells[n+1][PmultipleNumero][j][2],", ");
      od;
      Print(pMultipleTorsionCells[n+1][PmultipleNumero][
	Length(pMultipleTorsionCells[n+1][PmultipleNumero])][2],".\n");
    od;
  od;
  return [pMultipleTorsionCells, Pmultiples];
end;


pairsIntersection := function(sortedData, celldata)
#########################################################################
## We establish a preliminary list of cell triples as candidates for   ##
## fusions, consisting of a pair of n-cells that admits precisely      ##
## one adjacent (n-1)-cell in common.                                  ##
## This pair and their common boundary cell constitute our cell triple.##
#########################################################################
local pMultipleTorsionCells, Pmultiples, n, j, f, s, vcd, fusionCandidates,
numberOfFusionCandidates, firstCell, secondCell, commonBoundary;
  pMultipleTorsionCells := sortedData[1];
  Pmultiples := sortedData[2];
  vcd := Length(celldata) -1;
  fusionCandidates := [];
  numberOfFusionCandidates := 0;

  for n in [0..vcd] do
    for j in [1..Length(Pmultiples[n+1])] do
      for f in [1..Length(pMultipleTorsionCells[n+1][j])] do
	firstCell := pMultipleTorsionCells[n+1][j][f];
        for s in [f+1..Length(pMultipleTorsionCells[n+1][j])] do
	  secondCell := pMultipleTorsionCells[n+1][j][s];
	  commonBoundary := 
		Set(celldata[n+1][firstCell[2]]!.BoundaryImage!.ListIFace);
	  IntersectSet( commonBoundary,
	    Set(celldata[n+1][secondCell[2]]!.BoundaryImage!.ListIFace)
	  );
	  if Size(commonBoundary) = 1 then
	    Print("The boundary of ",n,"-cell numero ",firstCell[2]," is ",
	      celldata[n+1][firstCell[2]]!.BoundaryImage!.ListIFace,
	      " and that of ",n,"-cell numero ",secondCell[2]," is ",
	      celldata[n+1][secondCell[2]]!.BoundaryImage!.ListIFace,
	      " so they have ",n-1,"-cell numero ",commonBoundary[1],
		" in common.\n");
	    numberOfFusionCandidates := numberOfFusionCandidates +1;
	    fusionCandidates[numberOfFusionCandidates] :=
	      [firstCell, secondCell, commonBoundary[1]];	
	  fi;
        od;	
      od;
    od;
  od;
  return fusionCandidates;
end;


bifurcationFreeCells := function(fusionCandidates, torsionCells, celldata)
##########################################################################
## We keep only those cell triples as candidates for a fusion,          ##
## that are not "bifurcated" in the p-torsion subcomplex :              ##
## Triples such that only n-cells adjacent to the (n-1)-cell            ##
## of the triple are the two n-cells of the triple.                     ##
##########################################################################
local firstCell, secondCell, boundaryCell, cellTriple, n, cell, torsionCell,
remainingFusionCandidates;
remainingFusionCandidates := Set(StructuralCopy(fusionCandidates));

  for cellTriple in fusionCandidates do 

    firstCell := cellTriple[1];
    secondCell := cellTriple[2];
    n := firstCell[1];
    boundaryCell := cellTriple[3];

      for torsionCell in torsionCells[n+1] do

	cell := celldata[n+1][torsionCell[2]];

	if boundaryCell in cell!.BoundaryImage!.ListIFace then

	  if torsionCell in [firstCell, secondCell] then ;
	  	# the cell triple remains a candidate for fusion.
	  else
		Print("The p-torsion subcomplex is bifurcated at the ",
			n-1,"-cell numero ",boundaryCell,".\n");
	  	RemoveSet(remainingFusionCandidates, cellTriple);
	  fi;
	fi;	
      od; 
  od; 
  return remainingFusionCandidates;
end;


IsInnerConjugate := function(G, A, B)
#################################################################
## Check if the subgroups A and B of G are conjugate inside G. ##
## For groups with high cardinality, we use GAP's function     ##
## IsConjugate(G, A, B), whilst for low cardinalities we       ##
## implement an algorithm that is faster for our matrix groups.##
#################################################################
local areConjugate, j, g, card, subcard, a, setOfElements, gAgInv, timeDifference;
 timeDifference := Runtime();
 if IsFinite(G) then
  card := Size(G); 
  if card < 1001 then

    j := 1;
    setOfElements := Set(G);
    areConjugate := false;
    subcard := Size(A);

    while j < card/subcard +1 and areConjugate = false do
	g := setOfElements[1];
	gAgInv := ConjugateGroup(A, g);

	if IsSubset(gAgInv, B) then
	  if IsSubset(B, gAgInv) then
		areConjugate := true;
	  fi;
	fi;
	for a in A do 
		RemoveSet(setOfElements, a*g);
	od;
	j := j+1;
    od;
  else areConjugate := IsConjugate(G, A, B);
  fi;  
 else areConjugate := IsConjugate(G, A, B);
 fi;
 Print("Computing conjugation took ",Runtime()-timeDifference," ms.\n");
 return areConjugate;
end;


getIdentifier := function( cell, boundaryCell, celldata)
##############################################################
## Retrieve the group element sending the cell to           ##
## a representative that is adjacent to the boundary cell.  ##
##############################################################
local n, boundaryIndex, j, identifiers, g;
    n := cell[1];
    identifiers := celldata[n+1][cell[2]]!.BoundaryImage!.ListElt;
    
    for j in [1..Length(identifiers)] do

      if celldata[n+1][cell[2]]!.BoundaryImage!.ListIFace[j]
	 = boundaryCell then

		boundaryIndex := j;
      fi;
    od;
    g := celldata[n+1][cell[2]]!.BoundaryImage!.ListElt[boundaryIndex];
  return g;
end;


checkStabilizerConjugacy := function(fusionCandidates, celldata, p)
##################################################################
## Eliminate cell triples from the fusion candidates list,      ##
## for which the stabilizers of the n-cells are not conjugate.  ##
## Therefore, we first conjugate the stabilizers of the n-cells ##
## into stabilizers of representatives                          ##
## adjacent to the (n-1)-cell.                                  ##
## Then we check if we can conjugate them within the stabilizer ## 
## of the (n-1)-cell.     					##
##################################################################
local firstCell, secondCell, boundaryCell, firstStabilizer, secondStabilizer, boundaryStabilizer, cellTriple, n, g, validated, remainingFusionCandidates;
remainingFusionCandidates := StructuralCopy(fusionCandidates);

  for cellTriple in fusionCandidates do 

    firstCell := cellTriple[1];
    secondCell := cellTriple[2];
    n := firstCell[1];
    boundaryCell := cellTriple[3];
    firstStabilizer := celldata[n+1][firstCell[2]]!.TheMatrixStab;
    secondStabilizer := celldata[n+1][secondCell[2]]!.TheMatrixStab;
    boundaryStabilizer := celldata[n][boundaryCell]!.TheMatrixStab;
    g := getIdentifier( firstCell, boundaryCell, celldata);
    firstStabilizer := ConjugateGroup(firstStabilizer, g);
    g := getIdentifier( secondCell, boundaryCell, celldata);
    secondStabilizer := ConjugateGroup(secondStabilizer, g);

    if IsSubset( boundaryStabilizer, firstStabilizer) then
      if IsSubset( boundaryStabilizer, secondStabilizer) then

	if IsSubset(firstStabilizer, secondStabilizer) then
		validated := true;
	else
		validated := IsConjugate( boundaryStabilizer,
				 firstStabilizer, secondStabilizer);
	fi;
      else Print("****Error: ",n,"-cell numero ",secondCell[2],
		 " not pointwise fixed.****\n");
      fi;
    else Print("****Error: ",n,"-cell numero ",firstCell[2],
		 " not pointwise fixed.****\n");
    fi;
    if validated then 
      if IsSubset(firstStabilizer, boundaryStabilizer) then 
	;  ## in this case,  we can clearly merge the cell triple       ##
	   ## without changing p-primary equivariant Farrell cohomology ##
		Print("The ",n,"-cells numero ",firstCell[2]," and ",
		 secondCell[2]," are subject to a cell fusion.\n"); 
      else
        if IsPnormal( boundaryStabilizer, p) then
	 g := Normalizer(boundaryStabilizer, 
		Center(SylowSubgroup( boundaryStabilizer, p)
	 ));
	 if IsSubset(firstStabilizer, g) and IsSubset(g, firstStabilizer) then
		validated := true;
	   ## if firstStabilizer is isomorphic to g, it is proven       ##
	   ## that we can merge the cell triple                         ##
	   ## without changing p-primary equivariant Farrell cohomology ##
	  else
	  	validated := IsInnerConjugate(
				boundaryStabilizer, firstStabilizer, g);
	  fi; 
	  if validated then
		Print("The ",n,"-cells numero ",firstCell[2]," and ",
		 secondCell[2]," are subject to a non-trivial fusion.\n"); 
          fi;
        else
	 validated := false;
        fi;
      fi;
    fi;
    if validated then ; ## the cell triple is subject ot a cell fusion. ##

     else RemoveSet(remainingFusionCandidates, cellTriple);
    fi;
  od; 
  return remainingFusionCandidates;
end;


mergeCells := function( celldata, fusionCandidates, torsionCells)
####################################################################
## Merge the cells in that have been definitively retained        ##
## as fusion candidates. As proven, this does not change 	  ##
## the p-primary equivariant Farrell cohomology.		  ##
####################################################################
local firstCell, secondCell, boundaryCell, cellTriple, n, j, reducedTorsionCells, vcd, mergedBoundary;
  mergedBoundary := [];
  reducedTorsionCells := StructuralCopy( torsionCells);
  vcd := Length(reducedTorsionCells) -1;
  for n in [0..vcd] do
	reducedTorsionCells[n+1] := Set(reducedTorsionCells[n+1]);
	mergedBoundary[n+1] := [];
  od;
  for cellTriple in fusionCandidates do 

    firstCell := cellTriple[1];
    secondCell := cellTriple[2];
    n := firstCell[1];
    boundaryCell := cellTriple[3];
    Print("The ",n-1,"-cell numero ",boundaryCell,
	" is amalgamated into the merging ",n,"-cells numero ",
	firstCell[2]," and ",secondCell[2],".\n");
    RemoveSet(reducedTorsionCells[n], [n-1,boundaryCell]);
    mergedBoundary[n+1][firstCell[2]] := Set(Concatenation(
	celldata[n+1][firstCell[2]]!.BoundaryImage!.ListIFace,
	celldata[n+1][secondCell[2]]!.BoundaryImage!.ListIFace
    ));
    RemoveSet(mergedBoundary[n+1][firstCell[2]], boundaryCell);
    Print("The merged boundary of these two cells is ",
		mergedBoundary[n+1][firstCell[2]],".\n");
   # RemoveSet(mergedBoundary[n+1][firstCell[2]], boundaryCell);
    #Print(mergedBoundary[n+1][firstCell[2]]);	 
  od;
  return reducedTorsionCells;
end;


getTorsionSubcomplex := function(groupName, p)
#####################################################################
## Here, p is the prime for which to take the torsion subcomplex.  ##
## We extract the cells the stabilizer of which contains p-torsion.##
#####################################################################
local vcd, stabilizerCardinalities, celldata,
torsionCells, numberOfTorsionCells, n, j, returnedData, warned, groupname;
   groupname := Filtered( groupName, function ( x )
            return not (x = '(' or x = ')' or x = ',' or x = '[' or x = ']');
   end );
   Read(Concatenation( DirectoriesPackageLibrary("HAP")[1]![1], 
			"Perturbations/Gcomplexes/",groupname));
   celldata := StructuralCopy(HAP_GCOMPLEX_LIST);
   vcd := Length(celldata) -1;
   Print("Extracting the ",p,"-torsion subcomplex of the ",
		vcd,"-dimensional ",groupName,"-cell complex ... \n");
   returnedData := admissibilityCheck(celldata);
   stabilizerCardinalities := returnedData[1];
   warned := returnedData[2];
   torsionCells := [];
   numberOfTorsionCells := [];
   for n in [0..vcd] do
	torsionCells[n+1] := [];
	numberOfTorsionCells[n+1] := 0;
	for j in [1..Length(celldata[n+1])] do
	   ## Check if the stabilizer contains p-torsion ##
	   if stabilizerCardinalities[n+1][j] mod p = 0 then
		Print("Extracted ",n,"-cell numero ",j,
			" of stabilizer cardinality ",
			stabilizerCardinalities[n+1][j],".\n");
		numberOfTorsionCells[n+1] 
			:= numberOfTorsionCells[n+1]+1;
	        torsionCells[n+1][numberOfTorsionCells[n+1]]
			:=[n, j];
	   fi;
	od;
   od;
   return
     [torsionCells, numberOfTorsionCells, celldata, stabilizerCardinalities, warned];
end;


InstallGlobalFunction("TorsionSubcomplex", function(groupName, p)
############################################
local torsionCells, numberOfTorsionCells, celldata, sortedData, warned;

	sortedData := getTorsionSubcomplex(groupName, p);
	torsionCells := sortedData[1];
	numberOfTorsionCells := sortedData[2];
	celldata := sortedData[3];
	warned := sortedData[5];
	if IsPrime(p) then 
	  sortedData := [computeIncidenceMatrix(torsionCells, numberOfTorsionCells, celldata), warned];
	else
	  sortedData := "The number p must be prime in order to compute the incidence matrix.";
	fi;
   return
     sortedData;
end);

InstallGlobalFunction("VisualizeTorsionSkeleton", function(groupName, p)
##################################################
local incidenceMatrix, graphData, returnedData, warned;
    returnedData := TorsionSubcomplex(groupName, p);
    if IsPrime(p) then
	incidenceMatrix := returnedData[1];
	warned := returnedData[2];
	if warned = false then
	    Print("The quotient by the action on the 1-skeleton of the p-torsion subcomplex is ");
	else
	    Print("As the cell stabilizers do not fix the cells pointwise, \n",
		"we do not obtain the quotient by the action on the 1-skeleton of the p-torsion subcomplex.\n",
		" We obtain just some graph, and the latter is ");
	fi;
	if Size(incidenceMatrix) = 0 then
		Print("empty.\n");
	fi;
	if Size(incidenceMatrix) > 0 then
		Print("displayed in a separate window on screen now.\n");
		graphData := StructuralCopy(IncidenceMatrixToGraph(incidenceMatrix));
		GraphDisplay(graphData);
	fi;
   else Print("The number p must be prime in order to compute the incidence matrix.\n");
   fi;
end);

printIsotropyGroups := function(reducedTorsionCells, celldata) 
#########################################################
## Return the stabilizers of the cells in the          ##
## reduced torsion subcomplex.                         ##
#########################################################
local G, n, k, j, tcd;
   tcd := Length(reducedTorsionCells)-1;
   Print("The following cells and stabilizers represent ",
		"the reduced torsion subcomplex.\n");

   for n in [0..tcd] do
     for k in [1..Length(reducedTorsionCells[n+1])] do
	j := reducedTorsionCells[n+1][k][2];
	G :=   celldata[n+1][j]!.TheMatrixStab;
	if IsFinite(G) then
		Print(n,"-cell number ",j," has stabilizer ",G,"\n");
		Print("of Abelianization ",GroupHomology(G,1),".\n");
	fi;
     od;
   od;
end;	   


getTerminalVertices := function(reducedTorsionCells, celldata)
##########################################################################
## We single out the "terminal" vertices of the p-torsion subcomplex :  ##
## Those with exactly one adjacent edge.                                ##
##########################################################################
local examinedVertex, examinedEdge, examinedEdgeData, numberOfAdjacencies, 
	adjacentEdge, reductionCandidates, numberOfTerminalVertices;
  reductionCandidates:= []; numberOfTerminalVertices := 0;

  for examinedVertex in reducedTorsionCells[1] do 

    examinedVertex := examinedVertex[2];
    numberOfAdjacencies := 0;

    for examinedEdge in reducedTorsionCells[1+1] do
	examinedEdgeData := celldata[1+1][examinedEdge[2]];

	if examinedVertex in examinedEdgeData!.BoundaryImage!.ListIFace then

	  numberOfAdjacencies := numberOfAdjacencies +1;
	  adjacentEdge := examinedEdge[2];
	fi;
    od;
    if numberOfAdjacencies = 1 then 

	Print("The vertex number ",examinedVertex," is terminal.\n");
	numberOfTerminalVertices := numberOfTerminalVertices +1;
	reductionCandidates[numberOfTerminalVertices] := 
				[examinedVertex, adjacentEdge];
    fi;
  od; 
  return reductionCandidates;
end;



checkGruenSwan := function( terminalVertices, celldata, p)
#########################################################################
## Keep only those of the terminal vertices, the stabilizers of which  ##
## satisfy the hypotheses of the Gruen/Swan theorem.   	 	       ##
#########################################################################
local boundaryCell, examinedEdge, EdgeStabilizer, boundaryStabilizer, cellPair, n, g, validated, verticesToReduce;
  verticesToReduce := StructuralCopy( terminalVertices);

  for cellPair in terminalVertices do 

    boundaryCell := cellPair[1];
    examinedEdge := cellPair[2];
    EdgeStabilizer := celldata[1+1][examinedEdge]!.TheMatrixStab;
    boundaryStabilizer := celldata[0+1][boundaryCell]!.TheMatrixStab;
    g := getIdentifier( [1,examinedEdge], boundaryCell, celldata);
    EdgeStabilizer := ConjugateGroup(EdgeStabilizer, g);


    if IsSubset(EdgeStabilizer, boundaryStabilizer) then 
	validated := true;  
	   ## in this case,  we can clearly cut off the edge            ##
	   ## without changing p-primary equivariant Farrell cohomology ##
    else
      if IsPnormal( boundaryStabilizer, p) then
	g := Normalizer(boundaryStabilizer, 
		Center(SylowSubgroup( boundaryStabilizer, p)
	));
	if IsSubset(EdgeStabilizer, g) and IsSubset(g, EdgeStabilizer) then
		validated := true;
	else
	  	validated := IsInnerConjugate(
				boundaryStabilizer, EdgeStabilizer, g);
	fi; 
	   ## if firstStabilizer is isomorphic to g, it is proven       ##
	   ## that we can cut off the edge                              ##
	   ## without changing p-primary equivariant Farrell cohomology ##
      else
	validated := false;
      fi;
    fi;
    if validated then 
		Print("The terminal vertex ",boundaryCell,
		" with edge ",examinedEdge," is subject to a reduction.\n"); 
     else RemoveSet(verticesToReduce, cellPair);
    fi;
  od; 
  return verticesToReduce;
end;


InstallGlobalFunction("ReduceTorsionSubcomplex", function(groupName, p)
###################################################
local torsionCells, numberOfTorsionCells, celldata, sortedData, stabilizerCardinalities, fusionCandidates, reducedTorsionCells,
terminalVertices, warned;

   sortedData := getTorsionSubcomplex(groupName, p);
   torsionCells := sortedData[1];
   numberOfTorsionCells := sortedData[2];
   celldata := sortedData[3];
   stabilizerCardinalities := sortedData[4];
   warned := sortedData[5];
 
 if warned = false then
   sortedData := extractPmultipleTorsionCells(torsionCells, 
		numberOfTorsionCells, celldata,stabilizerCardinalities, p);
   fusionCandidates := pairsIntersection(sortedData, celldata);
   fusionCandidates := bifurcationFreeCells(fusionCandidates, torsionCells,
						 celldata);
   fusionCandidates := checkStabilizerConjugacy(fusionCandidates, celldata, 
						p);
   reducedTorsionCells := mergeCells( celldata, fusionCandidates, 
					torsionCells);
   terminalVertices := getTerminalVertices(reducedTorsionCells, celldata);
   terminalVertices := checkGruenSwan( terminalVertices, celldata, p); 
Print("\n At the following terminal vertices, the adjacent edge can be cut off without changing the equivariant Farrell cohomology : ",terminalVertices,"\n");  
   printIsotropyGroups(reducedTorsionCells, celldata);
 fi;
   # return reducedTorsionCells;
end);
