GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#(C) Graham Ellis, 2005-2006 ##################################################################### ##################################################################### InstallGlobalFunction(RankPrimeHomology, function(arg) local G,n, eltsG, gensG, Dimension, DimList, Boundary, PseudoBoundary, PseudoBoundaryAsVec, WordToVectorList, VectorListToWord, prime, pp, BoundaryMatrix, MT, GactMat, ZGbasisOfKernel, one, InverseFlat, ComplementaryBasis, zero, pcgens, g,h,i,x,xx,xxx,tmp; G:=arg[1]; if arg[2]=-1 then n:=1000; else n:=arg[2]; fi; tmp:=SSortedList(Factors(Order(G))); if Length(tmp)>1 then Print("This function can only be applied to small prime-power groups. \n"); return fail; fi; prime:=tmp[1]; pp:=Order(G); one:=Identity(GaloisField(prime)); zero:=0*one; gensG:=ReduceGenerators(GeneratorsOfGroup(G),G); eltsG:=Elements(G); MT:=MultiplicationTable(eltsG); pcgens:=Pcgs(G); pcgens:=ReduceGenerators(pcgens,G); pcgens:=List(pcgens,x->Position(eltsG,x)); DimList:=[]; PseudoBoundary:=[]; PseudoBoundaryAsVec:=[]; for i in [1..n] do PseudoBoundary[i]:=[]; PseudoBoundaryAsVec[i]:=[]; od; ##################################################################### Dimension:=function(i); if i<0 then return 0; fi; if i=0 then return 1; fi; return DimList[i]; end; ##################################################################### ##################################################################### Boundary:=function(i,j); if i<=0 then return []; fi; if j>0 then return PseudoBoundary[i][j]; else return NegateWord(PseudoBoundary[i][-j]); fi; end; ##################################################################### ##################################################################### WordToVectorList:=function(w,k) #w is a word in R_k. local v,x,a; #v is a list of vectors mod p. #v:=List([1..Dimension(k)],x->List([1..pp],y->0) ); v:=ListWithIdenticalEntries(Dimension(k),ListWithIdenticalEntries(pp,0) ); for x in w do a:=AbsoluteValue(x[1]); v[a][x[2]]:=v[a][x[2]] + SignInt(x[1]); od; return v mod prime; end; ##################################################################### for x in gensG do Add(PseudoBoundary[1], [[-1,1],[1,Position(eltsG,x)]] ); Add(PseudoBoundaryAsVec[1], Flat(WordToVectorList ( [[-1,1],[1,Position(eltsG,x)]] ,0))*one); od; DimList[1]:=Length(PseudoBoundary[1]); ##################################################################### VectorListToWord:=function(v) local w, i, x; w:=[]; for x in [1..Length(v)] do for i in [1..Length(v[x])] do if not v[x][i]=0 then Append(w, MultiplyWord(v[x][i],[ [x,i] ])); fi; od; od; return w; end; ##################################################################### ##################################################################### GactMat:=function(g,tB) local k,q,h,C; C:=[]; k:=0; for q in [0..(-1+Length(tB)/pp)] do for h in [1..pp] do C[k+MT[g][h]]:=tB[k+h]; od; k:=k+pp; od; ConvertToMatrixRepNC(C); return C; end; ##################################################################### ##################################################################### BoundaryMatrix:=function(k) #Returns the matrix of d_k:R_k->R_k-1 local B,M,r, b, i, g,j,gB; #M is actually the transpose of the matrix! B:=TransposedMat(PseudoBoundaryAsVec[k]); M:=[]; for g in [1..pp] do gB:=TransposedMat(GactMat(g,B)); for i in [0..Dimension(k)-1] do M[i*pp+g]:=gB[i+1]; od; od; return M; end; ##################################################################### ##################################################################### InverseFlat:=function(v) local w,x,cnt,i; w:=[]; cnt:=0; while cnt< Length(v) do x:=[]; for i in [cnt+1..cnt+pp] do Add(x,v[i]); cnt:=cnt+1; od; Add(w,x); od; return w; end; ##################################################################### ##################################################################### ComplementaryBasis:=function(arg) local B, NS, BC, heads,ln, i, v,zeroheads; B:=arg[1]; if Length(arg)>1 then NS:=arg[2]; fi; ConvertToMatrixRepNC(B,prime); B:=MutableCopyMat(B); heads:=SemiEchelonMatDestructive(B).heads; ln:=Length(B[1]); BC:=[]; zeroheads:=Filtered([1..ln],i->heads[i]=0); if Length(arg)=1 then for i in zeroheads do v:=ListWithIdenticalEntries(ln,zero); v[i]:=one; ConvertToVectorRep(v,prime); Add(BC,v); od; else for i in zeroheads do v:=NS.vectors[NS.heads[i]]; ConvertToVectorRep(v,prime); Add(BC,v); od; fi; return BC; end; ##################################################################### ##################################################################### ZGbasisOfKernel:=function(k) #The workhorse! local tB,i, v, g, h, b,bb, ln, B, B1, B2,NS, Bcomp, BndMat; BndMat:=BoundaryMatrix(k); PseudoBoundaryAsVec[k]:=0; PseudoBoundary[k]:=0; ConvertToMatrixRepNC(BndMat,prime); NS:=SemiEchelonMat(NullspaceMat(BndMat)); tB:=TransposedMat(NS.vectors); Bcomp:=ComplementaryBasis(NS.vectors); for g in pcgens do Append(Bcomp,SemiEchelonMat(TransposedMat(tB-GactMat(g,tB))).vectors); od; B1:=ComplementaryBasis(Bcomp,NS); NS:=0; #B1:=List(B1,v->List(v,x->IntFFE(x))); Apply(B1,v->List(v,x->IntFFE(x))); return B1; end; ##################################################################### for i in [2..n] do for x in ZGbasisOfKernel(i-1) do Add(PseudoBoundary[i], VectorListToWord(InverseFlat(x)) ); Add(PseudoBoundaryAsVec[i], x*one ); od; DimList[i]:=Length(PseudoBoundary[i]); if i>15 and arg[2]=-1 then x:=PoincareSeries(List([0..i],j->Dimension(j)),i+1); xx:=PoincareSeries(List([0..i-1],j->Dimension(j)),i); xxx:=PoincareSeries(List([0..i-2],j->Dimension(j)),i-1); if x=xx and xx=xxx and (not x=fail) then return x; fi; fi; od; ##################################################################### ##################################################################### return Dimension ; end); ##################################################################### ##################################################################### ##################################################################### ##################################################################### InstallGlobalFunction(EfficientNormalSubgroups, function(arg) local G, dim, EffSubgroups, N, x, R,S,T,D; ########################################################### G:=arg[1]; if Length(arg)>1 then dim:=arg[2]; else dim:=4; fi; ########################################################### ########################################################### if not IsPrimePowerInt(Order(G)) then Print("This function applies to prime-power groups only."); return fail; fi; ########################################################### N:=NormalSubgroups(G); N:=Filtered(N,x->(Order(x)>1 and Order(x)<Order(G))); EffSubgroups:=[]; R:=RankPrimeHomology(G,dim);; R:=List([0..dim],i->R(i)); for x in N do S:=ResolutionPrimePowerGroup(x,dim); T:=ResolutionPrimePowerGroup(G/x,dim); D:=ResolutionDirectProduct(S,T); D:=List([0..dim],i->D!.dimension(i)); if D=R then Add(EffSubgroups,x); fi; od; return EffSubgroups; end); ##################################################################### #####################################################################