
#Note:    The first part of this function written by Graham Ellis
#         The "homotpy" part is written by Bui Anh Tuan 

##########################################################################
#0
#F  sl2zngens.gi
##  Input: A G-equivalent CW-space with resolutions for all stabilisers 
##         a positive integer n. 
##  Output: The first n terms of a free ZG-resolution of Z. If the input 
##          together with a contracting homotopy then the output together 
##          with a contracting homotopy.
##
InstallGlobalFunction(FreeZGResolution,
function(arg)
local 
    P,N,prime,
    Dimension, DimensionRecord, DimRecs, FiltDimRecs, BinGp,
    Boundary, BoundaryP, Pair2Quad, Pair2QuadRec,
    Quad2Pair,Quad2PairRec, HtpyGen, HtpyWord,
    StabGrps, StabResls, ResolutionFG,
    action, AlgRed,  EltsG, G, Mult, MultRecord,
    DelGen, DelWord, DelGenRec,PseudoBoundary,FinalBoundary,
    FilteredLength, FilteredDimension, FilteredDimensionRecord,
    L,i,k,n,q,r,s,t,
    InducedHtpyGen, InducedHtpyWord, DelListSum, #Added Feb 2014 BUI A.T.
    Homotopy, HomotopyGen, NegateListWord, VertHtpy, 
    InducedHtpyList, IndHtpyRec;    

    SetInfoLevel(InfoWarning,0);

    P:=arg[1];
    N:=arg[2];
    if Length(arg)>2 then 
        prime:=Gcd(arg[3],EvaluateProperty(P,"characteristic"));
    else 
        prime:=EvaluateProperty(P,"characteristic"); 
    fi;

    N:=Minimum(EvaluateProperty(P,"length"),N);
    G:=P!.group;
    EltsG:=P!.elts;
    BoundaryP:=P!.boundary;

    BinGp:=ContractibleGcomplex("SL(2,O-2)_a");
    BinGp:=BinGp!.stabilizer(0,4);;
    BinGp:=Image(RegularActionHomomorphism(BinGp));

    ###################################################################     
    #1
    #F  ResolutionFG
    ##
    ##  Return resolutions for stabiliser groups if they are given
    ##  otherwise, find a new one.
    ##
    ##  Input:  group G and a positive integer n
    ##  Output: the first n+1 terms of a free ZG-resolution of Z  
    ##
    ResolutionFG:=function(G,n)
    local 
        x, tmp, iso,iso1,iso2,iso3,res,Q, fn;

        ##Added Jan 2012
        if IsBound(P!.resolutions) and HasName(G) then
            x:=Position(P!.resolutions[2], Name(G));
            if not x=fail then 
                return P!.resolutions[1][x]; 
            fi;
        fi;

        if Order(G)=infinity and IsAbelian(G) then
            #This will only be correct if G is abelian of "rank" equal
            #to the number of generators GAP has for G 

            res:=ResolutionGenericGroup(G,n);
            return res;
        fi;
        iso:=RegularActionHomomorphism(G);
        Q:=Image(iso);

        if IdGroup(Image(iso))=[24,3] then 
            iso1:=IsomorphismGroups(Q,BinGp); 
            res:=ResolutionFiniteGroup(BinGp,n);
            res!.group:=G;
            res!.elts:=List(res!.elts,x->
            PreImagesRepresentative(iso,PreImagesRepresentative(iso1,x)));
            return res;
        fi;

        res:=ResolutionFiniteGroup(Q,n); 
        res!.group:=G;
        res!.elts:=List(res!.elts,x->PreImagesRepresentative(iso,x));
        return res;
    end;    
    ############### end of ResolutionFG ###############################


    if prime>0 then 

        ###############################################################     
        #1
        #F  AlgRed
        ##
        ##  Algebraic reduction for list of words mod p
        ##
        ##  Input:  a list of words
        ##  Output: reduced list of words  
        ##
        AlgRed:= function(ww)
        local 
            w,x,v,pos,u;

            w:=StructuralCopy(ww);

            v:=Collected(w);
            for x in v do
                if x[1][1]<0 then 
                    x[1][1]:=-x[1][1]; x[2]:=-x[2] mod prime; 
                fi;
                if x[1][2]<0 then 
                    x[1][2]:=-x[1][2]; x[2]:=-x[2] mod prime; 
                fi;
                x[2]:=x[2] mod prime;
            od;

            u:=[];
            for x in v do
                Append(u,MultiplyWord(x[2],[x[1]]));
            od;

            v:=Collected(u);
            for x in v do
                x[2]:=x[2] mod prime;
            od;

            u:=[];
            for x in v do
                Append(u,MultiplyWord(x[2],[x[1]]));
            od;

            return u;
        end;
        ######################## end of AlgRed ########################

    else


        ###############################################################    
        #1
        #F  AlgRed
        ##
        ##  Algebraic reduction for list of words 
        ##
        ##  Input:  a list of words
        ##  Output: reduced list of words  
        ##
        AlgRed:= function(ww)
        local x,i,v,k,u,w;
            w:=ww;#w:=StructuralCopy(ww);

            for x in w do
                if x[2]<0 then 
                    x[1]:=-x[1];x[2]:=-x[2];
                fi;
            od;
            v:=Filtered(w,x->x[1]>0);
            for x in w do
                if x[1]<0 then
                    k:=Position(v,[-x[1],x[2],x[3]]);  
                    if (k=fail) then 
                        Add(v,x); 
                    else
                        Unbind(v[k]);
                    fi;
                fi;
            od;
            v:=Filtered(v,x->IsBound(x));

            return v;
        end;
        ###############################################################
    fi;



    ###################################################################
    if IsBound(P!.action) and not prime=2 then 
        action:=P!.action;
    else 
        action:=function(k,j,g) return 1; end;
    fi;
    ###################################################################

    MultRecord:=[];
    
    ###################################################################
    
    ###################################################################     
    #1
    #F  Mult
    ##
    ##  The product of 2 elements in EltsG 
    ##
    ##  Input:  a pair of positive integers (g,h)
    ##  Output: the position of the product in the list EltsG  
    ##
    Mult:=function(g,h)
    local pos;
        if not IsBound(MultRecord[g]) then 
            MultRecord[g]:=[];
        fi;
        if not IsBound(MultRecord[g][h]) then
            pos:= Position(EltsG,EltsG[g]*EltsG[h]);
            if pos=fail then 
                Add(EltsG,EltsG[g]*EltsG[h]);  
                MultRecord[g][h]:= Length(EltsG);
            else 
                MultRecord[g][h]:= pos; 
            fi;
        fi;
    return MultRecord[g][h];
    end;
    #######################end of Mult#################################

    # Create a list of stabiliser subgroups and their resolutions

    StabGrps:= List([0..Length(P)],n->
                List([1..P!.dimension(n)], k->P!.stabilizer(n,k))); 

    StabResls:=[];
    i:=N;
    if prime=0 then

        for L in StabGrps do
            Add(StabResls,List(L,    
                g->ExtendScalars(ResolutionFG(g,i),G,EltsG))
                );
            i:=Maximum(0,AbsInt(i-1));
        od;
    else
        for L in StabGrps do
            Add(StabResls,List(L,
            g->ExtendScalars(ResolutionFiniteGroup(g,i,false,prime),
            G,EltsG)));
            i:=Maximum(0,AbsInt(i-1));
        od;
    fi;

    DimRecs:=List([0..N],i->[]);

    ###################################################################
    
    ###################################################################     
    #1
    #F  Dimension
    ##
    ##  Find the ZG-rank of R_n 
    ##
    ##  Input:  a non-negative integer n
    ##  Output: ZG-rank of R_n  
    ##
    Dimension:=function(k)
    local 
        dim,i,R;
        dim:=0;
        for i in [0..k] do
            DimRecs[k+1][i+1]:=[];
            for R in StabResls[i+1] do
                dim:=dim+R!.dimension(k-i);
                Add(DimRecs[k+1][i+1],dim);
            od;
        od;
    return dim;
    end;
    ###################################################################
    
    DimensionRecord:=List([0..N],Dimension);

    Dimension:=function(k)
        return DimensionRecord[k+1];
    end;
    ###################################################################

    ###################################################################

    # Create a record for the function Quad2Pair
    
    Quad2PairRec:=[];
    for q in [0..N] do
        Quad2PairRec[q+1]:=[];
        for r in [1..Length(StabGrps[q+1])] do
            Quad2PairRec[q+1][r]:=[];
            for s in [0..N-q] do
                Quad2PairRec[q+1][r][s+1]:=[];
            od;
        od;
    od;
    ###################################################################


    ###################################################################     
    #1
    #F  Pair2Quad
    ##
    ##  The n-th generator in degree k of our final resolution is
    ##  actually the t-th generator in degree s of the resolution
    ##  of the r-th stabilizer group of the q-th chain module of the
    ##  non-free resolution. We need the function f(k,n)=[q,r,s,t]
    ##
    ##  Input:  a pair of integers (k,n)
    ##  Output: [q,r,s,t]  
    ##
    Pair2Quad:=function(k,n)
    local 
        qq,q,r,s,t;

        for qq in [0..N] do
            if n <= DimRecs[k+1][qq+1][Length(DimRecs[k+1][qq+1])] then 
                q:=qq; break; 
            fi;
        od;

        r:=PositionProperty(DimRecs[k+1][q+1],x->(n<=x));
        s:=k-q;
        if r-1>0 then
            t:=n-DimRecs[k+1][q+1][r-1];
        else
            if q>=1 then
                t:=n-DimRecs[k+1][q][Length(  DimRecs[k+1][q] )];;
            else 
                t:=n;
            fi;
        fi;
        Quad2PairRec[q+1][r][s+1][t]:=[k,n];
    return [q,r,s,t];
    end;
    ####################end of Pair2Quad ##############################

    # Create a record for the function Pair2Quad 

    Pair2QuadRec:=List([1..N+1],i->[]);
    for k in [0..N] do
        for n in [1..Dimension(k)] do
            Pair2QuadRec[k+1][n]:=Pair2Quad(k,n);
        od;
    od;
    ###################################################################

    ###################################################################     
    #1
    #F  Pair2Quad
    ##
    ##  Input:  a pair of integers (k,n)
    ##  Output: [q,r,s,t]  
    ##
    Pair2Quad:=function(k,n)
    local 
        a;
        if n>0 then
            return StructuralCopy(Pair2QuadRec[k+1][n]);
        else
            a:=StructuralCopy(Pair2QuadRec[k+1][-n]);
            a[4]:=-a[4];
            return a;
        fi;
    end;
    #################end of Pair2Quad##################################
    
    ###################################################################     
    #1
    #F  Quad2Pair 
    ##
    ##  Input:  [q,r,s,t]
    ##  Output: [k,n]  
    ##
    Quad2Pair:=function(q,r,s,t)
    local 
        a,pr,pt;
        if r>0 then 
            pr:=r;pt:=t;
        else
            pr:=-r;pt:=-t;
        fi;

        if pt>0 then
            return StructuralCopy(Quad2PairRec[q+1][pr][s+1][pt]);
        else
            a:=StructuralCopy(Quad2PairRec[q+1][pr][s+1][-pt]);
            a[2]:=-a[2];
            return a;
        fi;
    end;
    ######################end of Quad2pair ############################
    
    ###################################################################     
    #1
    #F  HtpyGen
    ##
    ##  This applies the "vertical homotopy" to the free group 
    ##  generator [r,t,g] in "dimension" [q,s].
    ##  The output is an "r-word" in "dimension" [q,s+1]. 
    ##
    ##  Input:  a free generato [r,t,g] in demension [q,s]
    ##  Output: an "r-word" in "dimension" [q,s+1]  
    ##
    HtpyGen:=function(q,s,r,t,g)
    local 
        y,pr,pt;

        if r>0 then 
            pr:=r;pt:=t;
        else
            pr:=-r;pt:=-t;
        fi;

        y:=StructuralCopy(StabResls[q+1][pr]!.homotopy(s,[pt,g]));
        Apply(y,x->[pr,x[1],x[2]]);
    return y;
    end;
    #######################end of HtpyGen##############################

    ###################################################################     
    #1
    #F  HtpyWord
    ##
    ##  This applies the "vertical homotopy" to the r-word w in 
    ##  “dimension” [q,s]. The output is an r-word in "dimension" [q,s+1].
    ##
    ##  Input: r-word w in “dimension” [q,s]
    ##  Output: an r-word in "dimension" [q,s+1] 
    ##
    HtpyWord:=function(q,s,w)
    local 
        h,z,x,y;
    #This applies the "vertical homotopy" to the r-word w in "dimension" 
    #[q,s]. The output is an r-word in "dimension" [q,s+1].

        h:=[];
        for y in w do
            x:=[action(q,y[1],y[3])*y[1],y[2],y[3]];
            z:=HtpyGen(q,s,x[1],x[2],x[3]);
            z:=List(z,a->[action(q,a[1],a[3])*a[1],a[2],a[3]]);
            Append(h,z);
        od;

    return AlgRed(h);
    end;
    #####################end of HtpyWord ##############################

    # Create a record for function DelGen

    DelGenRec:=[];
    for k in [1..N+1] do
        DelGenRec[k]:=[];
        for q in [1..N+1] do
            DelGenRec[k][q]:=[];
            for s in [1..N+1] do
                DelGenRec[k][q][s]:=[];
                for r in [1..P!.dimension(q-1)] do
                    DelGenRec[k][q][s][r]:=[];
                od;
            od;
        od;
    od;
    ###################################################################

    ###################################################################     
    #1
    #F  DelGen
    ##
    ##  For k=0,1,2 ... this is the equivariant homomorphism
    ##  Del_k:A_{q,s} ---> A_{q-k,s+k-1} applied to a free
    ##  r-generator [r,t] in dimension [q,s]. 
    ##
    ##  Input:  a positive integer k and 
    ##          a free r-generator [r,t] in dimension [q,s]
    ##  Output: a word in dimension [q-k,s+k-1].  
    ##
    DelGen:=function(k,q,s,r,t)
    local 
        y,pr,pt,i;

        if r>0 then 
            pr:=r;pt:=t;
        else
            pr:=-r;pt:=-t;
        fi;

        if IsBound(DelGenRec[k+1][q+1][s+1][pr][AbsInt(pt)]) then

            if pt>0 then return 
                DelGenRec[k+1][q+1][s+1][pr][pt];
            else
                return List(DelGenRec[k+1][q+1][s+1][pr][-pt], 
                                        a->[a[1],-a[2],a[3]]);
            fi;

        fi;

        if k=0 then
            if s=0 then 
                return [];
            else
                y:=List(StabResls[q+1][pr]!.boundary(s,pt),
                        x->[action(q,r,x[2])*x[1],x[2]]);
                if pt>0 then
                    DelGenRec[k+1][q+1][s+1][pr][pt]:= 
                            AlgRed(List(y,x->[pr,x[1],x[2]]));
                    return DelGenRec[k+1][q+1][s+1][pr][pt];
                else 
                    DelGenRec[k+1][q+1][s+1][pr][-pt]:=
                            AlgRed(List(y,x->[pr,-x[1],x[2]]));
                    return AlgRed(List(y,x->[pr,x[1],x[2]]));   
                fi;
            fi;
        fi;

        if k=1 then
            if s=0 then  
                if q=0 then 
                    return [];
                fi;
                y:=BoundaryP(q,pr);
                if pt>0 then 
                    DelGenRec[k+1][q+1][s+1][pr][pt]:= 
                                AlgRed(List(y,x->[x[1],1,x[2]]));
                    return DelGenRec[k+1][q+1][s+1][pr][pt];
                else
                    DelGenRec[k+1][q+1][s+1][pr][-pt]:= 
                                AlgRed(List(y,x->[x[1],1,x[2]]));
                    return List(y,x->[x[1],-1,x[2]]);
                fi;

            else

                if pt>0 then
                    DelGenRec[k+1][q+1][s+1][pr][pt]:=
                    AlgRed(HtpyWord(q-1,s-1,DelWord(1,q,s-1,
                                        DelGen(0,q,s,pr,-pt)))) ;
                    return DelGenRec[k+1][q+1][s+1][pr][pt];
                else
                    DelGenRec[k+1][q+1][s+1][pr][-pt]:=
                    AlgRed(HtpyWord(q-1,s-1,DelWord(1,q,s-1,
                                        DelGen(0,q,s,pr,pt)))) ;

                    return 
                        List(DelGenRec[k+1][q+1][s+1][pr][-pt], 
                                        a->[a[1],-a[2],a[3]]);
                fi;

            fi;
        fi;

        y:=[];
        for i in [1..k] do
            Append(y,
            HtpyWord(q-k,s+k-2,DelWord(i,q-k+i,s+k-i-1,
                                        DelGen(k-i,q,s,pr,-pt)))
            );
        od;
        y:=AlgRed(y);

        if pt>0 then
            DelGenRec[k+1][q+1][s+1][pr][pt]:=y;
        else
            DelGenRec[k+1][q+1][s+1][pr][-pt]:=List(y,a->
                                         [a[1],-a[2],a[3]]);
        fi;

    return y;
    end;
    #########################end of DelGen  ###########################

    ###################################################################     
    #1
    #F  DelWord
    ##
    ##  The product of 2 elements in Elts 
    ##
    ##  Input:  a pair of positive integers (i,j)
    ##  Output: the position of the product in the list Elts  
    ##
    DelWord:=function(k,q,s,w)
    local 
        y,x;
    #For k=0,1,2 ... this is the equivariant homomorphism
    #Del_k:A_{q,s} ---> A_{q-k,s+k-1} applied to an r-word [[r,t,g],...]
    #in dimension [q,s].

        y:=[];
        for x in w do
            Append(y,List(DelGen(k,q,s,x[1],x[2]),
                                    a->[a[1],a[2],Mult(x[3],a[3])]));
        od;

        return y;  #Added Jan 2013. Speeds up the calculation 
                   ## in some(!!) examples.
        return AlgRed(y);

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

    ###################################################################     
    #1
    #F  Boundary
    ##
    ##  Boundary map d_k: C_k -> C_{k-1} 
    ##
    ##  Input:  a pair of positive integers (k,n)
    ##  Output: The boundary d_k(f_n)  
    ##
    Boundary:=function(k,n)
    local 
        q,s,r,t,x,y,z,i;
        y:=Pair2Quad(k,n); 
        q:=y[1];s:=y[3];r:=y[2];t:=y[4];

        y:=[];

        for i in [0..k] do
            if q>=i then
                z:=DelGen(i,q,s,r,t);
                Append(y,List(z,x->
                        [Quad2Pair(q-i,x[1],s+i-1,x[2])[2],x[3]])  );
            else 
                break;
            fi;
        od;

    return AlgebraicReduction(y);
    end;
    ###################################################################

    PseudoBoundary:=[];
    for n in [1..N+1] do
        PseudoBoundary[n]:=[];
    od;

    ###################################################################     
    #1
    #F  FinalBoundary
    ##
    ##  Boundary map d_k: C_k -> C_{k-1}  
    ##
    ##  Input:  a pair of positive integers (k,n)
    ##  Output: the boundary d_k(f_n)  
    ##
    FinalBoundary:=function(n,k)
    local  
        pk;
        pk:=AbsInt(k);
        if  not IsBound(PseudoBoundary[n+1][pk]) then
            PseudoBoundary[n+1][pk]:= Boundary(n,pk);
        fi;
        if k>0 then  
            return PseudoBoundary[n+1][k];
        else  
            return NegateWord(PseudoBoundary[n+1][pk]); 
        fi;
    end;
    ###################################################################



    ################spectral sequence requirements#####################    


    FiltDimRecs:=[];
    for k in [0..N] do
        FiltDimRecs[k+1]:=[];
        for i in [1..Dimension(k)] do
            FiltDimRecs[k+1][i]:=Pair2Quad(k,i)[1];
        od;
    od;

    FilteredLength:=Maximum(Flat(FiltDimRecs));

    ###################################################################
    FilteredDimension:=function(r,k)

        return Length(Filtered(FiltDimRecs[k+1],x->x<=r));

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



    ########### BUI ANH TUAN - FEB 2014 ###############################



    ###################################################################     
    #1
    #F  InducedHtpyGen
    ##
    ##  This constructs the "induced homotopy" h1 from
    ##  the given homotopy of the non-free complex
    ##   h1: A_qs ->A_{q+1}s
    ##  This applies the "induced homotopy" to the free group generator
    ##   [r,t,g] in "dimension" [q,s].
    ##  The output is an "r-word" in "dimension" [q+1,s].
    ##
    ##  Input:  five integers
    ##  Output: an r-word in dimension [q+1,s].  
    ##
    InducedHtpyGen:=function(q,s,r,t,g)
    local 
        y,pr,pt,w,v;

        if r>0 then 
            pr:=r;pt:=t;
        else
            pr:=-r;pt:=-t;
        fi;

        y:=StructuralCopy(P!.homotopy(q,[pr,g]));
        if pt>0 then 
            Apply(y,x->[x[1],1,x[2]]);
        else 
            Apply(y,x->[x[1],-1,x[2]]);
        fi;
    return y;
    end;
    ###################################################################
    
    ###################################################################     
    #1
    #F  InducedHtpyWord
    ##
    ##  This applies the "induced homotopy" to the r-word w in  
    ##  "dimension" [q,s]. The output is an r-word in 
    ##  "dimension" [q+1,s].
    ##
    ##  Input:  dimension q,s and a word w
    ##  Output: an r-word in dimension [q+1,s]. 
    ##
    InducedHtpyWord:=function(q,s,w)
    local 
        h,z,x,y;


        h:=[];
        for y in w do
            x:=[y[1],y[2],y[3]];
            z:=StructuralCopy(InducedHtpyGen(q,s,x[1],x[2],x[3]));
            z:=List(z,a->[a[1],a[2],a[3]]);
            Append(h,z);
        od;

    return AlgRed(h);
    end;
    ###################################################################

    ###################################################################     
    #1
    #F  InducedHtpyList
    ##
    ##  This applies the Horizontal Homotopy to a list of words
    ##  For each word, this applies the "induced homotopy" to  
    ##  the r-word w in "dimension" [q,s]. 
    ##  The output is an r-word in "dimension" [q+1,s].
    ##
    ##  Input:  an r-word w in dimension [q,s]
    ##  Output: an r-word in dimension [q+1,s].  
    ##    
    InducedHtpyList:=function(w)
    local 
        h,z,x,y,v,b;
       
        h:=[];
        for y in w do 
            z:=StructuralCopy(InducedHtpyGen(y[1],y[2],y[3],y[4],y[5]));
            z:=List(z,a->[y[1]+1,y[2],a[1],a[2],a[3]]);
            Append(h,z);
        od;
        return h;
    end;
    ################################################################### 

    ############# d+=d1+d2+..+dq of a list of words####################
    
    ###################################################################     
    #1
    #F  DelListSum
    ##
    ##  Sum of DelWord_k where k from 1 to q
    ##
    ##  Input:  a word w
    ##  Output: the differential map d(w)  
    ##

    DelListSum:=function(w)
    local 
        y,d,x,h,k;
        h:=[];
        for x in w do
            y:=[];
            for k in [1..x[1]] do
                d:=StructuralCopy(DelGen(k,x[1],x[2],x[3],x[4]));
                Apply(d,v->[x[1]-k,x[2]+k-1,v[1],v[2],Mult(x[5],v[3])]);        
                Append(y,d);
            od;
            Append(h,y);
        od;

    return h;
    end;
    ################################################################### 
    
    ###################################################################     
    #1
    #F  VertHtpy
    ##
    ##  Applies to a list of [q,s,r,t,g]: could be in different A_qs
    ##  return a list of elements of the form [q,s,r,t,g]
    ##
    ##  Input:  [q,s,r,t,g]
    ##  Output: a list of elements of the form [q,s,r,t,g]. 
    ##
    VertHtpy:=function(w)

    local 
        h,x,y,v;
        h:=[];;
        for x in w do
            v:=[x[1],x[2],action(x[1],x[3],x[5])*x[3],x[4],x[5]];
            y:=StructuralCopy(HtpyGen(v[1],v[2],v[3],v[4],v[5]));
            Apply(y,a->[x[1],x[2]+1,action(x[1],a[1],a[3])*a[1],a[2],a[3]]);
            Append(h,y);
        od;    
    return h;
    end;
    ###################################################################  
    
    ###################################################################     
    #1
    #F  NegateListWord 
    ##
    ##  Input:  a list of elements of the form [q,s,r,t,g]
    ##  Output: the negated word.  
    ##

    NegateListWord:=function(w)
        Apply(w,x->[x[1],x[2],-x[3],x[4],x[5]]);    
    return w;
    end;
    ###################################################################
    
    ###################################################################     
    #1
    #F  HomotopyGen
    ##
    ##  The product of 2 elements in Elts 
    ##
    ##  Input:  [q,s,r,t,g]
    ##  Output: the homotopy h(q,s,r,t,g).  
    ##
    HomotopyGen:=function(arg)
    local 
        f,g,q,s,r,t,x,e,v,y,
        h0, h1, h0dh1, e3, h2, h;

        q:=arg[1];
        s:=arg[2];
        r:=arg[3];    
        t:=arg[4];
        g:=arg[5];

        if arg=[] then 
            return [];
        else
            if s = 0 then 
                h1:=StructuralCopy(InducedHtpyList([[q,s,r,t,g]]));
                h0dh1:=StructuralCopy(VertHtpy(DelListSum(h1)));
                v:=StructuralCopy(DelListSum(h0dh1));
                e3:=[];         # e3=h(d+)h0(d+)d1
                for x in v do
                    Append(e3,HomotopyGen(x[1],x[2],x[3],x[4],x[5]));
                od;
                e:=[];          # e=h1-h0(d+)h1+h(d+)h0(d+)h1
                Append(e,h1);
                Append(e,NegateListWord(h0dh1));
                Append(e,e3);
            elif s>0 then
            # s>0 then e=0
                e:=[];
            fi;
            y:=StructuralCopy([q,s,r,t,g]);

            h0:=VertHtpy([y]);

            v:=DelListSum(h0);
            h2:=[];         # h2=h(d+)h0
            for x in v do
                Append(h2,HomotopyGen(x[1],x[2],x[3],x[4],x[5]));
            od;
            h:=[];         # h=h0-d(d+)h0 + e
            Append(h,h0);
            Append(h,NegateListWord(h2));
            Append(h,e);
            return h;
        fi;
    end;

    
    ###################################################################     
    #1
    #F  Homotopy
    ## 
    ##
    ##  Input:  degree k an a word w
    ##  Output: the homotopy h_k(w).  
    ##

    Homotopy:=function(k,w)
    local 
        f,g,q,s,r,t,v,e,h;
    ### h([])=[] 
        if w=[] then 
            return [];
        fi;
        f:=w[1];
        g:=w[2];
        v:=Pair2Quad(k,f);
        q:=v[1];
        r:=v[2];
        s:=v[3];
        t:=v[4];

        h:=HomotopyGen(q,s,r,t,g);
        Apply(h,x->[Quad2Pair(x[1],x[3],x[2],x[4])[2],x[5]]);
    return AlgebraicReduction(h);
    end;
    ###################################################################

SetInfoLevel(InfoWarning,1);

#######################################################################
return Objectify(HapResolution,
                rec(
                inputresl:=P,
                verthtpy:=VertHtpy,
                htpy:=HtpyWord,
                delword:=DelWord,
                dimension:=Dimension,
                filteredDimension:=FilteredDimension,
                boundary:=FinalBoundary,
        inducedhomotopy:=InducedHtpyGen,
        stabrels:=StabResls,
                homotopy:=Homotopy,
                elts:=P!.elts,
                group:=P!.group,
                pseudoBoundary:=PseudoBoundary,
                properties:=
                   [["length",N],
                    ["filtration_length",FilteredLength],
                    ["initial_inclusion",true],
                    ["reduced",true],
                    ["type","resolution"],
                    ["characteristic",prime]  ]));


end);

################### end of FreeZGResolution ############################

