#(C) Graham Ellis 2005-2006

#####################################################################
InstallGlobalFunction(PolytopalComplex,
function(arg)
local
	G,StartVector, Gev,
	PG,
	action,
	VertexToVector, VVRecord,
	FaceToVertices,
	Hasse,
	p,x,n,i,
	Points,
	Dimension,
	Boundary,
        InsertSigns,
        StandardWord,
        StandardWordSgn,
	lngth,ln,
	StabilizerSubgroup,
	StabilizerRecord,
        StabilizerBasisRecord,
        StabilizerBasis,
	BoundaryComponent,
	EltsG,
	PseudoBoundary,
	OrbitReps,
	StabSum,
	StabAction,
	VSGS;

G:=arg[1];
if IsMatrixGroup(G) then
for x in GeneratorsOfGroup(G) do
if not IsOne(x*TransposedMat(x)) then
Print("The representation is not orthogonal.\n");
return fail;
fi;
od;
fi;

StartVector:=arg[2];
PG:=PolytopalGenerators(G,StartVector);

if Length(arg)>2 then lngth:=arg[3]; else lngth:=Length(PG.hasseDiagram); fi;
Points:=[];
EltsG:=Elements(G);
VSGS:=VectorStabilizer(G,StartVector);

#####################################################################
Dimension:=function(k)
if k<0 then return 0; fi;
if k=0 then return 1; fi;
if k>lngth then return 0; fi;
return Length(Hasse[k]);
end;
#####################################################################

if IsPermGroup(G) then
#####################################################################
action:=function(g,V)
return Permuted(V,g^-1);
end;
#####################################################################
else
#####################################################################
action:=function(g,V)
return g*V;    
end;
#####################################################################
fi;


#########################CREATE POINTS###############################
for x in G do
Add(Points, action(x,StartVector));
od;
Points:=SSortedList(Points);
#####################################################################


#####################################################################
VertexToVector:=function(v)
return action(PG.generators[v+1],StartVector) - StartVector;
end;
#####################################################################


#####################################################################
FaceToVertices:=function(F)
local W,v,w,V;
#RT:=RT-Runtime();
V:=[];
W:=BaseOrthogonalSpaceMat(List(F,x->VertexToVector(x)));

if W=[] then
W:=[Points[1]*0];
fi;

for p in Points do

if IsZero((p - StartVector)*TransposedMat(W)) then Add(V,p); fi;
od;
return V;
end;
#####################################################################


Hasse:=[];
for x in [1..lngth] do
Append(Hasse,[List(PG.hasseDiagram[x],y->FaceToVertices(y))     ]);
od;

#Print(Hasse,"\n");

#####################################################################
OrbitReps:=function(L)  #L=Hasse[i]
local g,R,S, T,Reps,bool,count;

Reps:=[];
for S in L do
bool:=true;
count:=0;
   for g in G do
   count:=count+1;
   T:=List(S,x->action(g,x));
      for R in Reps do
      if Length(T)=Length(Intersection(T,R)) then
      bool:=false; break; fi;
      od;
   if bool =false then break;fi;
   if count=Order(G) then Add(Reps,SSortedList(S)); fi;
   od;
od;

return Reps;
end;
#####################################################################


Hasse:=List(Hasse,x->OrbitReps(x));




StabilizerRecord:=List([1..lngth],i->[1..Dimension(i)]);
StabilizerBasisRecord:=List([1..lngth],i->[1..Dimension(i)]);


#####################################################################
StabilizerSubgroup:=function(kk,nn)
local S,T,xT,verts,StabGroup,x,k,n;

k:=AbsInt(kk);
n:=AbsInt(nn);

if k=0 then return VSGS; fi;

if not IsInt(StabilizerRecord[k][n]) then
return StabilizerRecord[k][n]; fi;

if k=Length(PG.hasseDiagram) then return G; fi;

StabGroup:=Group(One(G));
S:=Hasse[k][n];

T:=StructuralCopy(S);
for x in G do
if not x in StabGroup then
xT:=List(T,a->action(x,a));
if Length(Intersection(xT,T))=Length(T) then 
StabGroup:=  GeneratorsOfGroup(StabGroup); ;
StabGroup:=Concatenation(StabGroup,[x]);
StabGroup:=Group(StabGroup);
fi;
fi;
od;


StabGroup:=ReduceGenerators(GeneratorsOfGroup(StabGroup),StabGroup);
if Length(StabGroup)=0 then StabGroup:=[Identity(G)]; fi;

StabilizerRecord[k][n]:=Group(StabGroup);
return StabilizerRecord[k][n];
end;
#####################################################################

#####################################################################
StabilizerBasis:=function(kk,nn)
local S,T,verts,bas,CG,x,k,n;

k:=AbsInt(kk);
n:=AbsInt(nn);

if k=0 then return []; fi;

if not IsInt(StabilizerBasisRecord[k][n]) then
return StabilizerBasisRecord[k][n]; fi;

if k=Length(PG.hasseDiagram) then return IdentityMat(Length(StartVector)); fi;

S:=Hasse[k][n];

CG:=Sum(S)/Length(S);
bas:=List(S,x->x-CG);
bas:=SemiEchelonMat(bas).vectors;

StabilizerBasisRecord[k][n]:=bas;
return StabilizerBasisRecord[k][n];
end;
#####################################################################



StabSum:=List([1..lngth],k->
Sum(List([1..Dimension(k-1)],j->Order(StabilizerSubgroup(k-1,j)))-1));



#####################################################################
BoundaryComponent:=function(k,m,n)  	#Let Fm be the m-th face in 
					#dimension k, and Fn the n-th
					#face in dimension k-1. Return 
					#a list [g1,...,gd] of the elements
					#gi in G such that gi.Fn lies in the
					#boundary of Fm. The list is maximal
					#with respect to the property that
					#gi*gj^-1 is not in the stabilizer
					#of Fn.
local 	Fm,Fn, Stab, Component, CompCpy,test,
	g, gFn;

Fm:=Hasse[k][m];
if k>1 then Fn:=Hasse[k-1][n];
else Fn:=[StartVector]; fi;
Stab:=StabilizerSubgroup(k-1,n);

Component:=[];
CompCpy:=[];

for g in G do
gFn:=SSortedList(List(Fn,x->action(g,x)));
if Size(gFn) = Size(Set(Intersection(gFn,Fm))) then
if not gFn in CompCpy then
Add(Component,g); 
Add(CompCpy,gFn);
fi;
fi;
od;

return SSortedList(Component);
end;
#####################################################################

#####################################################################
StandardWord:=function(k,bnd)
local w;
w:=
List(bnd,x->[x[1],
Position(EltsG,  CanonicalRightCosetElement(StabilizerSubgroup(k,AbsInt(x[1])), EltsG[x[2]]^-1 )^-1)
]);
return AlgebraicReduction(w);
end;
#####################################################################

#####################################################################
StandardWordSgn:=function(k,bnd)
local w,x,y,r,h;
w:=[];
for x in bnd do 
r:=CanonicalRightCosetElement(StabilizerSubgroup(k,AbsInt(x[1])), EltsG[x[2]]^-1 )^-1;
y:=[x[1]*StabAction(k,AbsInt(x[1]),x[2]), Position(EltsG,r)];
Add(w,y);
od;
return AlgebraicReduction(w);
end;
#####################################################################


PseudoBoundary:=List([1..lngth],i->[1..Dimension(i)]);

#####################################################################
Boundary:=function(k,mm)
local b,bb,x,n, bnd,signedbnd,bndbnd,tmp,m;

if k<1 then return []; fi;  #Added April 2017
m:=AbsoluteValue(mm);

if not IsInt(PseudoBoundary[k][m]) then 
if mm>0 then return PseudoBoundary[k][m]; 
else return NegateWord(PseudoBoundary[k][m]);fi;
fi;

bnd:=[];
for n in [1..Dimension(k-1)] do
tmp:=BoundaryComponent(k,m,n);
tmp:=List(tmp, x->Position(EltsG,x));    ##########
tmp:=List(tmp, x->[n,x]);
Append(bnd,tmp);
od;

bnd:= StandardWord(k-1,bnd); 

PseudoBoundary[k][m]:=bnd;

if mm>0 then return PseudoBoundary[k][m];
else return NegateWord(PseudoBoundary[k][m]);fi;

end;
###############################################################

###############################################################
# This describes how the group G acts on the orientation.
StabAction:=function(n,k,h)
local bas, Gbas, mat,id,r,u,H; 

if n=0 then return 1; fi;

H:=StabilizerSubgroup(n,k);

id:=CanonicalRightCosetElement(H,Identity(G));
r:=CanonicalRightCosetElement(H,EltsG[h]^-1);
r:=id^-1*r;
u:=r*EltsG[h];

bas:=StabilizerBasis(n,k);
Gbas:=List(bas,V->action(u,V));
mat:=List(Gbas, b->SolutionMat(bas,b));

return Determinant(mat);
end;
###############################################################

for n in [1..lngth] do
for i in [1..Dimension(n)] do
Boundary(n,i);
od;od;

##We now need to insert signs into the boundary.
###############################################################
InsertSigns:=function()
local i, bnd, copybnd, b, sb, pos, signedbnd,bndbnd, n, w, D;

for i in [1..Dimension(1)] do
bnd:=SortedList(PseudoBoundary[1][i]);;
bnd[2][1]:=-bnd[2][1];
PseudoBoundary[1][i]:=bnd;
od;

for n in [2..lngth] do
for i in [1..Dimension(n)] do

#####################################
#####################################
bnd:=SSortedList(PseudoBoundary[n][i]);;  #regular CW space
copybnd:=1*bnd;
signedbnd:=[];
D:=[];
bndbnd:=[];
for x in bnd do
w:=Boundary(n-1,x[1]);
w:=List(w, y->[y[1],Position(EltsG,EltsG[x[2]]*EltsG[y[2]]) ]);
w:=StandardWordSgn(n-2,w);
Add(bndbnd,w);
od;
#####################################
#####################################

signedbnd:=[1*bnd[1]];
D:=1*bndbnd[1];
RemoveSet(bnd,bnd[1]);

############
while Length(bnd)>0 do


for b in bnd do
pos:=Position(copybnd,b);
if Length(Intersection(D,bndbnd[pos]))>0 then break; fi;
if Length(Intersection(D,NegateWord(bndbnd[pos])))>0 then pos:=-pos; break; fi;
od;


if pos<0 then Add(signedbnd,1*b); 
D:=AddFreeWords(D,bndbnd[-pos]);
else Add(signedbnd,1*[-b[1],b[2]]); 
D:=AddFreeWords(D,NegateWord(bndbnd[pos]));
fi;
RemoveSet(bnd,b);

od;
############

PseudoBoundary[n][i]:=signedbnd;
#Print(Collected(D),"\n\n");


od;
od;
end;

InsertSigns();
###############################################################
##Signs inserted.
if Length(arg)>2 then ln:=lngth; else ln:=1000; fi;
#####################################################################
return Objectify(HapNonFreeResolution,
	   rec(
            dimension:=Dimension,
            boundary:=Boundary,
            homotopy:=fail,
            elts:=EltsG,
            group:=G,
            standardWord:=StandardWord,
	    stabilizer:=StabilizerSubgroup,
            basis:=StabilizerBasis,
	    action:=StabAction,
	    hasse:=Hasse,
            originalGroup:=arg[1],
            properties:=
             [["type","resolution"],
              ["length",ln],
              ["characteristic", 0] ]));

end);
#####################################################################

