1#############################################################################
2##
3##  This file is part of GAP, a system for computational discrete algebra.
4##  This file's authors include Werner Nickel, Alexander Hulpke.
5##
6##  Copyright of GAP belongs to its developers, whose names are too numerous
7##  to list here. Please refer to the COPYRIGHT file for details.
8##
9##  SPDX-License-Identifier: GPL-2.0-or-later
10##
11##  This file contains the implementation of the methods for SchurMultiplier
12##  and Darstellungsgruppen.
13##
14
15##    Take a finite presentation F/R for a group G and compute a presentation
16##    of one of G's representation groups (Darstellungsgruppen, Schur covers).
17##    This is done by assembling a presentation for F/[R,F] and then finding a
18##    generating set for a complement C/[R,F] for the intersection of R and
19##    [F,F] in R/[R,F].
20##
21##    No attempt is made to reduce the number of generators in the
22##    presentation.  This can be done using the Tietze routines from the GAP
23##    library.
24
25BindGlobal("SchurCoverFP",function( G )
26local g, i, m, n, r, D, I, M, M2,fgens,rels,gens,Drels,nam;
27
28  fgens:=FreeGeneratorsOfFpGroup(G);
29  rels:=RelatorsOfFpGroup(G);
30  n := Length( fgens );
31  m := Length( rels );
32  nam:=List(fgens,String);
33  if not ForAny(nam,x->'k' in x) then
34    r:="k";
35  else
36    r:=First(Concatenation(CHARS_LALPHA,CHARS_UALPHA),
37      x->not ForAny(nam,y->x in y));
38    if r=fail then
39      r:="extra"; # unlikely to have the same name, will just print weirdly
40      # but not calculate wrongly
41    else
42      r:=[r];
43    fi;
44  fi;
45
46  for i in [1..m] do
47    Add(nam,Concatenation(r,String(i)));
48  od;
49
50  D := FreeGroup(nam);
51  gens:=GeneratorsOfGroup(D);
52  Drels := [];
53  for i in [1..m] do
54    r := rels[i];
55    Add(Drels, MappedWord( r, fgens, gens{[1..n]} ) / gens[n+i] );
56  od;
57  for g in gens{[1..n]} do
58    for r in gens{[n+1..n+m]} do
59      Add( Drels, Comm( r, g ) );
60    od;
61  od;
62
63  M := [];
64  for r in rels do
65    Add( M, List( fgens, g->ExponentSumWord( r, g ) ) );
66  od;
67
68  M{[1..m]}{[n+1..n+m]} := IdentityMat(m);
69  M := HermiteNormalFormIntegerMat( M );
70  M:=Filtered(M,i->not IsZero(i));
71
72  r := 1; i := 1;
73  while r <= m and i <= n do
74    while i <= n and M[r][i] = 0 do
75      i := i+1;
76    od;
77    if i <= n then  r := r+1; fi;
78  od;
79  r := r-1;
80
81  if r > 0 then
82    M2 := M{[1..r]}{[n+1..n+m]};
83    M2 := HermiteNormalFormIntegerMat( M2 );
84    M2:=Filtered(M2,i->not IsZero(i));
85    for i in [1..Length(M2)] do
86      Add(Drels,LinearCombinationPcgs(gens{[n+1..n+m]},M2[i]));
87    od;
88  fi;
89
90  # make the group
91  D:=D/Drels;
92  return D;
93end);
94
95InstallMethod(SchurCover,"of fp group",true,[IsSubgroupFpGroup],0,
96  SchurCoverFP);
97
98InstallMethod(EpimorphismSchurCover,"generic, via fp group",true,[IsGroup],1,
99    function(G)
100    local iso,
101          hom,
102          F,D,p,gens,Fgens,Dgens;
103
104    ## Check to see if G is trivial -- if so then just return
105    ## the map from the trivial FP group and G.
106    if IsTrivial(G) then
107        F := FreeGroup(1);
108        D := F/[F.1];
109        return GroupHomomorphismByImages(
110                   D,  G,
111                   GeneratorsOfGroup(D), AsSSortedList(G));
112    fi;
113    ##
114    ##
115    iso:=IsomorphismFpGroup(G);
116    F:=ImagesSource(iso);
117    Fgens:=GeneratorsOfGroup(F);
118    D:=SchurCoverFP(F);
119
120  # simplify the fp group
121  p:=PresentationFpGroup(D);
122  Dgens:=GeneratorsOfPresentation(p);
123  TzInitGeneratorImages(p);
124  TzOptions(p).printLevel:=0;
125  TzGo(p);
126  D:=FpGroupPresentation(p);
127  gens:=TzPreImagesNewGens(p);
128  Dgens:=List(gens,i->MappedWord(i,Dgens,
129    Concatenation(Fgens,List([1..(Length(Dgens)-Length(Fgens))],
130                             j->One(F)))));
131
132  hom:=GroupHomomorphismByImagesNC(D,G,GeneratorsOfGroup(D),
133   List(Dgens,i->PreImagesRepresentative(iso,i)));
134  Dgens:=TzImagesOldGens(p);
135  Dgens:=List(Dgens{[Length(Fgens)+1..Length(Dgens)]},
136           i->MappedWord(i,p!.generators,GeneratorsOfGroup(D)));
137  SetKernelOfMultiplicativeGeneralMapping(hom,SubgroupNC(D,Dgens));
138
139  return hom;
140end);
141
142
143# compute commutators and their images so that we know the image on `mul',
144# create out relations v=v^g.
145BindGlobal("CommutGenImgs",function(pcgs,g,h,mul)
146local u,a,b,i,j,c,x,y;
147  u:=TrivialSubgroup(mul);
148  a:=[];
149  b:=[];
150  x:=One(mul);
151  y:=One(h[1]);
152  repeat
153    for i in [1..Length(g)] do
154      for j in [1..i-1] do
155	c:=Comm(g[i],g[j]^x);
156	if not c in u then
157	  Add(a,c);
158	  Add(b,Comm(h[i],h[j]^y));
159	  u:=ClosureGroup(u,c);
160	  if IsSubgroup(u,mul) then
161	    a:=CanonicalPcgsByGeneratorsWithImages(pcgs,a,b);
162	    return List(GeneratorsOfGroup(mul),
163		i->i/PcElementByExponentsNC(a[2],ExponentsOfPcElement(a[1],i)));
164	  fi;
165	fi;
166      od;
167    od;
168    #in rare cases we also need commutators of conjugates.
169    if Size(mul)=1 then
170      return [];
171    else
172      Info(InfoSchur,2,"the commutators do not generate!");
173      i:=Random(1,Length(g));
174      x:=x*g[i];
175      y:=y*h[i];
176    fi;
177  until false;
178end);
179
180InstallGlobalFunction(SchuMu,function(g,p)
181local s,pcgs,n,iso,H,l,cov,der,pco,ng,gens,imgs,ran,zer,i,j,e,a,
182      mult,rels,de,epi,mul,hom,dc,q,qs,mq;
183  s:=SylowSubgroup(g,p);
184  if IsCyclic(s) then
185    return InverseGeneralMapping(IsomorphismPcGroup(s));
186  fi;
187
188  pcgs:=Pcgs(s);
189  n:=Normalizer(g,s);
190  l:=LogInt(Size(s),p);
191
192  # compute a Darstellungsgruppe as PC-Group
193  de:=EpimorphismSchurCover(s);
194
195  # exponent of M(G) is at most p^(n/2)
196  epi:=EpimorphismPGroup(Source(de),p,PClassPGroup(s)+Int(l/2));
197  cov:=Range(epi);
198  mul:=Image(epi,KernelOfMultiplicativeGeneralMapping(de));
199  if Size(mul)=1 then
200    return InverseGeneralMapping(IsomorphismPcGroup(s));
201  fi;
202
203  # get a decent pcgs for the cover
204  pco:=List(pcgs,i->Image(epi,PreImagesRepresentative(de,i)));
205  Append(pco,Pcgs(mul));
206  pco:=PcgsByPcSequenceNC(FamilyObj(One(cov)),pco);
207
208  # the induced action of n on the derived subgroup of the cover:
209  # we prescribe images on the commutator factor group. These may not be
210  # entirely correct -- multiplicator elements are missing. However on [G,G]
211  # they are unique -- the wrong central parts cancel out
212  # (use Burnside's basis theorem)
213
214  der:=DerivedSubgroup(cov);
215  ng:=GeneratorsOfGroup(n);
216  gens:=[];
217  imgs:=List(ng,i->[]);;
218  ran:=[1..Length(pcgs)];
219  zer:=ListWithIdenticalEntries(Length(pco)-Length(pcgs),0);
220  for i in pco do
221    Add(gens,i);
222    a:=PcElementByExponentsNC(pcgs,ExponentsOfPcElement(pco,i){ran});
223    for j in [1..Length(ng)] do
224      e:=ExponentsOfPcElement(pcgs,a^ng[j]);
225      Append(e,zer);
226      Add(imgs[j],PcElementByExponentsNC(pco,e));
227    od;
228  od;
229
230  # now we add new relators: x^g=x for all central x
231  rels:=TrivialSubgroup(cov);
232  for j in [1..Length(ng)] do
233    # extend homomorphically
234    rels:=ClosureGroup(rels,CommutGenImgs(pco,gens,imgs[j],mul));
235  od;
236
237  if Size(rels)=Size(mul) then
238    # total vanish
239    return InverseGeneralMapping(IsomorphismPcGroup(s));
240  fi;
241
242  # form the quotient, make it the new cover and the new multiplicator.
243  hom:=NaturalHomomorphismByNormalSubgroupNC(cov,rels);
244  mul:=Image(hom,mul);
245  cov:=Image(hom,cov);
246  pco:=List(pco{[1..Length(pcgs)]},i->Image(hom,i));
247  Append(pco,Pcgs(mul));
248  pco:=PcgsByPcSequenceNC(FamilyObj(One(cov)),pco);
249  epi:=GroupHomomorphismByImagesNC(cov,s,pco,
250         Concatenation(pcgs,List(Pcgs(mul),i->One(s))));
251  SetKernelOfMultiplicativeGeneralMapping(epi,mul);
252
253  # now extend to the full group
254  rels:=TrivialSubgroup(cov);
255  dc:=List(DoubleCosetRepsAndSizes(g,n,n),i->i[1]);
256  i:=1;
257  while i<=Length(dc) and Index(mul,rels)>1 do
258    if Order(dc[i])>1 then # the trivial element will not do anything
259      q:=Intersection(s,ConjugateSubgroup(s,dc[i]^-1));
260      if Size(q)>1 then
261	qs:=PreImage(epi,q);
262	# factor generators
263	gens:=GeneratorsOfGroup(qs);
264	# their conjugates
265	imgs:=List(gens,j->PreImagesRepresentative(epi,Image(epi,j)^dc[i]));
266	rels:=ClosureGroup(rels,CommutGenImgs(pco,gens,imgs,
267			    Intersection(mul,DerivedSubgroup(qs))));
268      fi;
269    fi;
270    i:=i+1;
271  od;
272  hom:=NaturalHomomorphismByNormalSubgroupNC(cov,rels);
273  mul:=Image(hom,mul);
274  cov:=Image(hom,cov);
275  pco:=List(pco{[1..Length(pcgs)]},i->Image(hom,i));
276  Append(pco,Pcgs(mul));
277  pco:=PcgsByPcSequenceNC(FamilyObj(One(cov)),pco);
278  epi:=GroupHomomorphismByImagesNC(cov,s,pco,
279         Concatenation(pcgs,List(Pcgs(mul),i->One(s))));
280  SetKernelOfMultiplicativeGeneralMapping(epi,mul);
281  return epi;
282
283end);
284
285InstallMethod(AbelianInvariantsMultiplier,"naive",true,
286  [IsGroup],1, G->AbelianInvariants(KernelOfMultiplicativeGeneralMapping(EpimorphismSchurCover(G))));
287
288InstallMethod(AbelianInvariantsMultiplier,"via Sylow Subgroups",true,
289  [IsGroup],0,
290function(G)
291local a,f,i;
292  Info(InfoWarning,1,"Warning: AbelianInvariantsMultiplier via Sylow subgroups is under construction");
293  a:=[];
294  f:=Filtered(Collected(Factors(Size(G))),i->i[2]>1);
295  for i in f do
296    Append(a,AbelianInvariants(KernelOfMultiplicativeGeneralMapping(
297               SchuMu(G,i[1]))));
298  od;
299  return a;
300end);
301
302# <hom> is a homomorphism from a finite group onto an fp group. It returns
303# an isomorphism from the same group onto an isomorphic fp group <F>, such
304# that no negative exponent occurs in the relators of <F>.
305#
306BindGlobal("PositiveExponentsPresentationFpHom",function(hom)
307local G,F,geni,ro,fam,r,i,j,rel,n,e;
308  G:=Image(hom);
309  F:=FreeGeneratorsOfFpGroup(G);
310  geni:=List(GeneratorsOfGroup(G),i->PreImagesRepresentative(hom,i));
311  ro:=List(geni,Order);
312  fam:=FamilyObj(F[1]);
313  r:=[];
314  for i in RelatorsOfFpGroup(G) do
315    rel:=[];
316    for j in [1..NrSyllables(i)] do
317      n:=GeneratorSyllable(i,j);
318      Add(rel,n);
319      e:=ExponentSyllable(i,j);
320      if e<0 then
321        e:=e mod ro[n];
322      fi;
323      Add(rel,e);
324    od;
325    Add(r,ObjByExtRep(fam,rel));
326  od;
327  # ensure the relative orders are relators.
328  for i in [1..Length(ro)] do
329    if not F[i]^ro[i] in r then
330      Add(r,F[i]^ro[i]);
331    fi;
332  od;
333  # new fp group
334  F:=FreeGroupOfFpGroup(G)/r;
335  hom:=GroupHomomorphismByImagesNC(Source(hom),F,geni,GeneratorsOfGroup(F));
336  return hom;
337end);
338
339InstallGlobalFunction(CorestEval,function(FG,s)
340# This has plenty of space for optimization.
341local G,H,D,T,i,j,k,l,a,h,nk,evals,rels,gens,r,np,g,invlist,el,elp,TL,rp,pos;
342
343  G:=Image(FG);
344  H:=Image(s);
345  D:=Source(s);
346  Info(InfoSchur,2,"lift index:",Index(G,H));
347  T:=RightTransversal(G,H);
348  TL:=List(T,i->i); # we need to refer to the elements very often
349
350  rels:=RelatorsOfFpGroup(Source(FG));
351  gens:=List(GeneratorsOfGroup(Source(FG)),i->Image(FG,i));
352
353  # this will guarantee we always take the same preimages
354  el:=AsSSortedListNonstored(H);
355  elp:=List(el,i->PreImagesRepresentative(s,i));
356  #ensure the preimage of identity is one
357  if IsOne(el[1]) then
358    pos:=1;
359  else
360    pos:=Position(el,One(H));
361  fi;
362  elp[pos]:=One(elp[pos]);
363
364  # deal with inverses
365  invlist:=[];
366  for g in gens do
367    h:=One(D);
368    for k in T do
369      np:=k*g;
370      nk:=TL[PositionCanonical(T,np)];
371      h:= h*elp[Position(el,np/nk)]*elp[Position(el,nk/g/k)];;
372    od;
373    Add(invlist,h);
374  od;
375
376  evals:=[];
377
378  for rp in [1..Length(rels)] do
379
380    CompletionBar(InfoSchur,2,"Relator Loop: ",rp/Length(rels));
381    r:=rels[rp];
382    i:=LetterRepAssocWord(r);
383    a:=One(D);
384
385    # take care of inverses
386    for l in [1..Length(i)] do
387      if i[l]<0 then
388	#i[l]:=-i[l];
389	a:=a*invlist[-i[l]];
390      fi;
391    od;
392
393    for j in [1..Length(T)] do
394
395      k:=T[j];
396      h:=One(D);
397      for l in i do
398	if l<0 then
399	  g:=Inverse(gens[-l]);
400	else
401	  g:=gens[l];
402	fi;
403	np:=k*g;
404	nk:=TL[PositionCanonical(T,np)];
405	#h:=h*PreImagesRepresentative(s,np/nk);
406	h:=h*elp[Position(el,np/nk)];
407	k:=nk;
408      od;
409
410      #Print(PreImagesRepresentative(s,Image(s,h))*h,"\n");
411      #a:=a/PreImagesRepresentative(s,Image(s,h))*h;
412      a:=a/h*elp[Position(el,Image(s,h))];
413
414    od;
415    Add(evals,[r,a]);
416  od;
417  CompletionBar(InfoSchur,2,"Relator Loop: ",false);
418  return evals;
419end);
420
421InstallGlobalFunction(RelatorFixedMultiplier,function(hom,p)
422local G,B,P,F,FH,U,s,D,shom,i,j,v,r,ri,iso,rank,bas,basr,row,rel,sol,
423      Dg,Dgi,car,dgh,snf,mat;
424  G:=Source(hom);
425  rank:=Length(GeneratorsOfGroup(G));
426  B:=ImagesSource(hom);
427  P:=SylowSubgroup(B,p);
428  # the corresponding free group (where the relators live)
429  F:=FreeGroupOfFpGroup(G);
430  FH:=GroupHomomorphismByImagesNC(F,B,FreeGeneratorsOfFpGroup(G),
431        List(GeneratorsOfGroup(G),i->Image(hom,i)));
432
433  s:=SchuMu(B,p);
434  D:=Source(s);
435  ri:=CorestEval(hom,s);
436
437  # now rel is a list of relators and their images in M(B).
438  # find relator relations in F/F' and evaluate these in M(B) to find
439  # M_R(B).
440  bas := [];
441  basr := [];
442  mat:=[];
443  for rel in ri do
444    row := ListWithIdenticalEntries(rank,0);
445    for i  in [1..NrSyllables(rel[1])]  do
446      j := GeneratorSyllable(rel[1],i);
447      row[j]:=row[j]+ExponentSyllable(rel[1],i);
448    od;
449    Add(mat,row);
450  od;
451  # SNF
452  snf:=NormalFormIntMat(mat,15);
453  mat:=mat*snf.coltrans; # changed coordinates (parent presentation)
454  bas:=snf.rowtrans*mat;
455  v:=Filtered([1..Length(bas)],i-> not IsZero(bas[i]));
456  # express the basis elements
457  bas:=bas{v};
458  basr:=[];
459  for i in v do
460    rel:=One(Source(s));
461    for j in [1..Length(mat)] do
462      rel:=rel*ri[j][2]^snf.rowtrans[i][j];
463    od;
464    Add(basr,rel);
465  od;
466
467  # now collect relations
468  v:=TrivialSubgroup(D);
469  for i in [1..Length(mat)] do
470    sol:=SolutionMat(bas,mat[i]);
471    rel:=ri[i][2];
472    for j in [1..Length(sol)] do
473      rel:=rel/basr[j]^sol[j];
474    od;
475    if not rel in v then
476      #NC is safe
477      v:=ClosureSubgroupNC(v,rel);
478    fi;
479  od;
480
481  for i in basr do
482    for j in basr do
483      # NC is safe
484      v:=ClosureSubgroupNC(v,Comm(i,j));
485    od;
486  od;
487
488  Info(InfoSchur,1,"Extra central part:",
489       Index(KernelOfMultiplicativeGeneralMapping(s),v));
490  # form the quotient
491  j:=NaturalHomomorphismByNormalSubgroupNC(D,v);
492  i:=GeneratorsOfGroup(Image(j));
493  i:=GroupHomomorphismByImagesNC(Image(j),P,i,
494       List(i,k->ImageElm(s,PreImagesRepresentative(j,k))));
495  SetKernelOfMultiplicativeGeneralMapping(i,
496    Image(j,KernelOfMultiplicativeGeneralMapping(s)));
497  return i;
498
499end);
500
501BindGlobal("MulExt",function(G,pl)
502local hom,	#isomorphism fp
503      ng,ngl,	# nr generators,list
504      s,sl,	# SchuMu,list
505      ab,ms,	# abelian invariants, multiplier size
506      pll,	# relevant primes
507      F,	# free group
508      rels,	# relators
509      rel2,	# cohomology relators
510      ce,	# corestriction
511      p,pp,	# prime, index
512      mg,	# multiplier generators
513      sdc,	# decomposition function
514      gens,free,# generators
515      i,j,	# loop
516      q,qhom;	# quotient
517
518
519
520  # eliminate useless primes
521  pl:=Intersection(pl,
522        List(Filtered(Collected(Factors(Size(G))),i->i[2]>1),i->i[1]));
523
524  hom:=IsomorphismFpGroup(G);
525  hom:=hom*IsomorphismSimplifiedFpGroup(Image(hom));
526  Info(InfoSchur,2,Length(RelatorsOfFpGroup(Range(hom)))," relators");
527
528  # think positive...
529  #if SYF then
530  #  hom:=PositiveExponentsPresentationFpHom(hom);
531  #fi;
532
533  hom:=InverseGeneralMapping(hom);
534  ng:=Length(GeneratorsOfGroup(Source(hom)));
535
536  sl:=[];
537  ngl:=[ng];
538  pll:=[];
539  ms:=1;
540  for p in pl do
541    s:=SchuMu(G,p);
542    if Size(KernelOfMultiplicativeGeneralMapping(s))>1 then
543      Add(pll,p);
544      Add(sl,SchuMu(G,p));
545      ab:=AbelianInvariants(KernelOfMultiplicativeGeneralMapping(s));
546      ms:=ms*Product(ab);
547      Add(ngl,ngl[Length(ngl)]+Length(ab));
548    fi;
549  od;
550  Info(InfoSchur,1,"Relevant primes:",pll);
551  Info(InfoSchur,1,"Multiplicator size:",ms);
552  if Length(pll)=0 then
553    return IdentityMapping(G);
554  fi;
555
556  #F:=FreeGroup(List([1..ngl[Length(ngl)]],x->Concatenation("@",String(x))));
557  F:=FreeGroup(ngl[Length(ngl)]);
558
559  rels:=[];
560  rel2:=[];
561  for pp in [1..Length(pll)] do
562    p:=pll[pp];
563    Info(InfoSchur,2,"Cohomology for prime :",p);
564    s:=sl[pp];
565    mg:=IsomorphismPermGroup(KernelOfMultiplicativeGeneralMapping(s));
566    mg:=List(IndependentGeneratorsOfAbelianGroup(Image(mg)),
567	  i->PreImagesRepresentative(mg,i));
568    sdc:=ListWithIdenticalEntries(ngl[Length(ngl)],One(Source(s)));
569    sdc{[ngl[pp]+1..ngl[pp+1]]}:=mg;
570
571    sdc:=GroupHomomorphismByImagesNC(F,KernelOfMultiplicativeGeneralMapping(s),
572	  GeneratorsOfGroup(F),sdc);
573
574    gens:=GeneratorsOfGroup(F){[ngl[pp]+1..ngl[pp+1]]};
575    ce:=CorestEval(hom,s);
576
577    for i in gens do
578      Add(rels,i^Order(Image(sdc,i)));
579      for j in GeneratorsOfGroup(F) do
580	if i<>j then
581	  Add(rels,Comm(i,j));
582	fi;
583      od;
584    od;
585
586    q:=[];
587    for i in ce do
588      Add(q,PreImagesRepresentative(sdc,i[2]));
589    od;
590    rel2[pp]:=q;
591  od;
592
593  # now run through the last ce
594  gens:=GeneratorsOfGroup(F){[1..ng]};
595  free:=FreeGeneratorsOfFpGroup(Source(hom));
596  for i in [1..Length(ce)] do
597    q:=One(F);
598    for j in [1..Length(pll)] do
599      q:=q*rel2[j][i];
600    od;
601    Add(rels,MappedWord(ce[i][1],free,gens)/q);
602  od;
603
604  q:=F/rels;
605  if AssertionLevel()>0 then
606    if Size(q)<>Size(G)*ms then
607      Error("oops!");
608    fi;
609  else
610    SetSize(q,Size(G)*ms);
611  fi;
612  qhom:=GroupHomomorphismByImages(q,G,GeneratorsOfGroup(q),
613          Concatenation(List(GeneratorsOfGroup(Source(hom)),i->Image(hom,i)),
614	    List([ng+1..Length(GeneratorsOfGroup(q))],
615	         i->One(G)) ));
616  SetIsSurjective(qhom,true);
617  SetSize(Source(qhom),Size(G)*ms);
618
619  return qhom;
620end);
621
622DoMulExt:=function(arg)
623local G,pl;
624  G:=arg[1];
625  if not IsFinite(G) then
626    Error("cover is only defined for finite groups");
627  elif Size(G)=1 then
628    return IdentityMapping(G);
629  elif IsPGroup(G) then
630    TryNextMethod(); # we recursively call the algorithm for the p-sylow
631  fi;
632  Info(InfoWarning,1,"Warning: EpimorphismSchurCover via Holt's algorithm is under construction");
633  if Length(arg)>1 then
634    pl:=arg[2];
635  else
636    pl:=PrimeDivisors(Size(G));
637  fi;
638  return MulExt(G,pl);
639end;
640
641InstallMethod(EpimorphismSchurCover,"Holt's algorithm",true,[IsGroup],0,
642 DoMulExt);
643
644InstallOtherMethod(EpimorphismSchurCover,"Holt's algorithm, primes",true,
645  [IsGroup,IsList],0,DoMulExt);
646
647InstallMethod(SchurCover,"general: Holt's algorithm",true,[IsGroup],0,
648  G->Source(EpimorphismSchurCover(G)));
649
650############################################################################
651############################################################################
652##
653##  Additional attributes and properties                     Robert F. Morse
654##  derived from computing the Schur Cover
655##  of a group.
656##
657##  A Epicentre
658##  O NonabelianExteriorSquare
659##  O EpimorphismNonabelianExteriorSquare
660##  P IsCapable
661##
662############################################################################
663##
664#A  Epicentre(<G>)
665##
666##  There are various ways of describing the epicentre of a group. It is
667##  the smallest normal subgroup $N$ of $G$ such that $G/N$ is a central
668##  quotient of some group $H$. It is also the exterior center of a group.
669##
670InstallMethod(Epicentre,"Naive Method",true,[IsGroup],0,
671    function(G)
672        local epi;
673        epi := EpimorphismSchurCover(G);
674        return Image(epi,Center(Source(epi)));
675    end
676);
677
678#############################################################################
679##
680#A  Epicentre(G,N)
681##
682##  Place holder attribute for computing the epicentre relative to a normal
683##  subgroup $N$. This is an attribute of $N$.
684##
685InstallOtherMethod(Epicentre,"Naive method",true,[IsGroup,IsGroup],0,
686    function(G,N)
687        TryNextMethod();
688    end
689);
690
691#############################################################################
692##
693#O  NonabelianExteriorSquare
694##
695##  Computes the Nonabelian Exterior Square $G\wedge G$ of a group $G$.
696##  For finitely generated groups this is the derived subgroup of the
697##  Schur cover -- which is an invariant for all Schur covers of group.
698##
699InstallMethod(NonabelianExteriorSquare, "Naive method", true, [IsGroup],0,
700    G->DerivedSubgroup(SchurCover(G)));
701
702#############################################################################
703##
704#O  EpimorphismNonabelianExteriorSquare(<G>)
705##
706##  Computes the mapping $G\wedge G \to G$. The kernel of this
707##  mapping is isomorphic to the Schur Multiplicator.
708##
709InstallMethod(EpimorphismNonabelianExteriorSquare, "Naive method", true,
710    [IsGroup],0,
711    function(G)
712        local epi, ## Epimorphism from the Schur cover to G
713              D;   ## Derived subgroup of the Schur Cover
714
715        epi := EpimorphismSchurCover(G);
716        D   := DerivedSubgroup(Source(epi));
717
718        ## Compute the restricted mapping of epi from
719        ## D --> G
720        ##
721        ## Need to check that D is trivial i.e. has no generators.
722        ## In this case we create the homomorphism using the group's
723        ## elements rather than generators.
724        ##
725        if IsTrivial(D) then
726
727            return GroupHomomorphismByImages(
728                       D, Image(epi,D),
729                       AsSSortedList(D), AsSSortedList(Image(epi,D)));
730        fi;
731
732        return GroupHomomorphismByImages(
733                   D, Image(epi,D),
734                   GeneratorsOfGroup(D),
735                   List(GeneratorsOfGroup(D),x->Image(epi,x)));
736
737    end
738);
739
740#############################################################################
741##
742#P  IsCentralFactor(<G>)
743##
744##  Dertermines if $G$ is a central factor of some group $H$ or not.
745##
746InstallMethod(IsCentralFactor, "Naive method", true, [IsGroup], 0,
747    G -> IsTrivial(Epicentre(G)));
748