1#############################################################################
2##
3##  This file is part of GAP, a system for computational discrete algebra.
4##  This file's authors include Gábor Horváth, Stefan Kohl, Markus Püschel, Sebastian Egner.
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 a method for determining structure descriptions for
12##  given finite groups and implementations of related functionality.
13##
14##  The purpose of this method is to give a human reader a rough impression
15##  of the group structure -- it does neither determine the group up to
16##  isomorphism (this would make the description for larger groups quite long
17##  and difficult to read) nor is it usually the only ``sensible''
18##  description for a given group.
19##
20##  The code has been translated, simplified and extended by Stefan Kohl
21##  from GAP3 code written by Markus Püschel and Sebastian Egner.
22##
23
24
25#############################################################################
26##
27#M  IsTrivialNormalIntersectionInList( <L>, <U>, <V> ) . . . . generic method
28##
29InstallGlobalFunction( IsTrivialNormalIntersectionInList,
30
31  function( MinNs, U, V )
32    local N, g;
33
34    for N in MinNs do
35      g := First(GeneratorsOfGroup(N), g -> g<>One(N));
36      if g <> fail and g in U and g in V then
37        return false;
38      fi;
39    od;
40    return true;
41  end);
42
43#############################################################################
44##
45#M  IsTrivialNormalIntersection( <G>, <U>, <V> ) . . . . . . . generic method
46##
47
48InstallMethod( IsTrivialNormalIntersection,
49               "if minimal normal subgroups are computed", IsFamFamFam,
50               [ IsGroup and HasMinimalNormalSubgroups, IsGroup, IsGroup ],
51
52  function( G, U, V )
53    local N, g;
54
55    for N in MinimalNormalSubgroups(G) do
56      g := First(GeneratorsOfGroup(N), g -> g<>One(N));
57      if g <> fail and g in U and g in V then
58        # found a nontrivial common element
59        return false;
60      fi;
61    od;
62    # if U and V intersect nontrivially, then their intersection must contain
63    # a minimal normal subgroup, and therefore both U and V contains any of
64    # its generators
65    return true;
66  end);
67
68InstallMethod( IsTrivialNormalIntersection,
69               "generic method", IsFamFamFam,
70               [ IsGroup, IsGroup, IsGroup ],
71
72  function( G, U, V )
73
74    return IsTrivial(NormalIntersection(U, V));
75  end);
76
77#############################################################################
78##
79#M  UnionIfCanEasilySortElements( <L1>[, <L2>, ... ] ) . . . . generic method
80##
81InstallGlobalFunction( UnionIfCanEasilySortElements,
82
83  function( arg )
84    local i, N;
85
86    for i in [1..Length(arg)] do
87      for N in arg[i] do
88        if not CanEasilySortElements(N) then
89          return Concatenation(arg);
90        fi;
91      od;
92    od;
93    return Union(arg);
94  end);
95
96#############################################################################
97##
98#M  AddSetIfCanEasilySortElements( <list>[, <obj> ) . . . . . generic method
99##
100InstallGlobalFunction( AddSetIfCanEasilySortElements,
101
102  function( list, obj )
103
104    if CanEasilySortElements( list ) and IsSet( list ) then
105      AddSet( list, obj );
106    else
107      Add( list, obj );
108    fi;
109  end);
110
111#############################################################################
112##
113#M  NormalComplement( <G>, <N> ) . . . . . . . . . . . generic method
114##
115InstallMethod( NormalComplement,
116               "generic method", IsIdenticalObj, [ IsGroup,  IsGroup ],
117
118  function( G, N )
119
120    # if <N> is trivial then the only complement is <G>
121    if IsTrivial(N) then
122      return G;
123
124    # if <G> and <N> are equal then the only complement is trivial
125    elif G = N  then
126      return TrivialSubgroup(G);
127
128    elif not (IsSubgroup(G, N) and IsNormal(G, N)) then
129      Error("N must be a normal subgroup of G");
130
131    else
132      return NormalComplementNC(G, N);
133    fi;
134  end);
135
136InstallMethod( NormalComplementNC,
137               "generic method", IsIdenticalObj, [ IsGroup,  IsGroup ],
138
139  function( G, N )
140
141    local F,    # G/N
142          DfF,  # Direct factors of F=G/N
143          gF,   # element of F=G/N
144          g,    # element of G corresponding to gF
145          x,    # element of G
146          i,    # running index
147          l,    # list for storing stuff
148          b,    # elements of abelian complement
149          C,    # Center of G
150          S,    # Subgroup of C
151          r,    # RationalClass of C
152          R,    # right coset
153          gens, # generators of subgroup of C
154          B,    # complement to N
155          T,    # = [C_G(N), G] = 1 x B'
156          Gf,   # = G/T = N x B/B'
157          Nf,   # = NT/T
158          Bf,   # abelian complement to Nf in Gf ( = B/B')
159          BfF;  # Direct factors of Bf
160
161    # if <N> is trivial then the only complement is <G>
162    if IsTrivial(N) then
163      return G;
164
165    # if <G> and <N> are equal then the only complement is trivial
166    elif G = N  then
167      return TrivialSubgroup(G);
168
169    # if G/N is abelian
170    elif HasAbelianFactorGroup(G, N) then
171      F := FactorGroupNC(G, N);
172      b := [];
173      l := [];
174      i:=0;
175      for gF in IndependentGeneratorsOfAbelianGroup(F) do
176        i := i+1;
177        g := PreImagesRepresentative(NaturalHomomorphism(F), gF);
178        R := RightCoset(N, g);
179        # DirectFactorsOfGroup already computed Center and RationalClasses
180        # when calling NormalComplement
181        if HasCenter(G) and HasRationalClasses(Center(G)) then
182          l := [];
183          C := Center(G);
184          for r in RationalClasses(C) do
185            if Order(Representative(r)) = Order(gF) then
186              for x in Set(r) do
187                if x in R then
188                  l := [x];
189                  break;
190                fi;
191              od;
192            fi;
193          od;
194          # Intersection(l, R) can take a long time
195          l := First(l, x -> true);
196        # if N is big, then Center is hopefully small and fast to compute
197        elif HasCenter(G) or Size(N) > Index(G, N) then
198          C := Center(G);
199          # it is enough to look for the Order(gF)-part of C
200          gens := [];
201          for x in IndependentGeneratorsOfAbelianGroup(C) do
202            Add(gens, x^(Order(x)/GcdInt(Order(x), Order(gF))));
203          od;
204          S := SubgroupNC(C, gens);
205          if Size(S) > Size(N) then
206            # Intersection(S, R) can take a long time
207            l := First(R, x -> Order(x) = Order(gF) and x in S);
208          else
209            # Intersection(C, R) can take a long time
210            l := First(S, x -> Order(x) = Order(gF) and x in R);
211          fi;
212        # N is small, then looping through its elements might be more
213        # efficient than computing the Center
214        else
215          l := First(R, x -> Order(x) = Order(gF)
216                          and IsCentral(G, SubgroupNC(G, [x])));
217        fi;
218        if l <> fail then
219          b[i] := l;
220        else
221          return fail;
222        fi;
223      od;
224      B := SubgroupNC(G, b);
225      return B;
226
227    # if G/N is not abelian
228    else
229      T := CommutatorSubgroup(Centralizer(G, N), G);
230      if not IsTrivial(T) and IsTrivialNormalIntersection(G, T, N) then
231        Gf := FactorGroupNC(G, T);
232        Nf := Image(NaturalHomomorphism(Gf), N);
233        # not quite sure if this check is needed
234        if HasAbelianFactorGroup(Gf, Nf) then
235          Bf := NormalComplementNC(Gf, Nf);
236          if Bf = fail then
237            return fail;
238          else
239            B := PreImage(NaturalHomomorphism(Gf), Bf);
240            return B;
241          fi;
242        else
243          return fail;
244        fi;
245      else
246        return fail;
247      fi;
248    fi;
249  end);
250
251#############################################################################
252##
253#M  DirectFactorsOfGroup( <G> ) . . . . . . . . . . . . . . .  generic method
254##
255InstallMethod(DirectFactorsOfGroup,
256            "for direct products if normal subgroups are computed", true,
257            [ IsGroup and HasDirectProductInfo and HasNormalSubgroups ], 0,
258
259  function(G)
260    local i, info, Ns, MinNs, H, Df, DfNs, DfMinNs, N, g, gs;
261
262    Ns := NormalSubgroups(G);
263    MinNs := MinimalNormalSubgroups(G);
264    Df := [];
265    info := DirectProductInfo(G).groups;
266    for i in [1..Length(info)] do
267      H := Image(Embedding(G,i),info[i]);
268      DfMinNs := Filtered(MinNs, N ->IsSubset(H, N));
269      if Length(DfMinNs) = 1 then
270        # size of DfMinNs is an upper bound to the number of components of H
271        Df := UnionIfCanEasilySortElements(Df, [H]);
272      else
273        DfNs := Filtered(Ns, N ->IsSubset(H, N));
274        gs := [ ];
275        for N in DfMinNs do
276          g := First(GeneratorsOfGroup(N), g -> g<>One(N));
277          if g <> fail then
278            AddSetIfCanEasilySortElements(gs, g);
279          fi;
280        od;
281        # normal subgroup containing all minimal subgroups cannot have complement in H
282        DfNs := Filtered(DfNs, N -> not IsSubset(N, gs));
283        Df := UnionIfCanEasilySortElements(Df,
284                            DirectFactorsOfGroupFromList(H, DfNs, DfMinNs));
285      fi;
286    od;
287    return Df;
288  end);
289
290InstallMethod(DirectFactorsOfGroup, "for direct products", true,
291                      [ IsGroup and HasDirectProductInfo ], 0,
292
293  function(G)
294    local i, info, Ns;
295
296    Ns := [];
297    info := DirectProductInfo(G).groups;
298    for i in [1..Length(info)] do
299      Ns := UnionIfCanEasilySortElements(Ns,
300                        DirectFactorsOfGroup(Image(Embedding(G,i),info[i])));
301    od;
302    return Ns;
303  end);
304
305InstallMethod(DirectFactorsOfGroup, "if normal subgroups are computed", true,
306                      [ IsGroup and HasNormalSubgroups ], 0,
307
308  function(G)
309    local Ns, MinNs, GGd, g, N, gs;
310
311    Ns := NormalSubgroups(G);
312    MinNs := MinimalNormalSubgroups(G);
313
314    if Length(MinNs)= 1 then
315      # size of MinNs is an upper bound to the number of components
316      return [ G ];
317    fi;
318
319    if IsSolvableGroup(G) then
320      GGd := CommutatorFactorGroup(G);
321      if IsCyclic(GGd) and IsPrimePowerInt(Size(GGd)) then
322        # G is direct indecomposable, because has a unique maximal subgroup
323        return [ G ];
324      fi;
325    else
326      GGd := CommutatorFactorGroup(G);
327      # if GGd is not cyclic of prime power size then there are at least two
328      # maximal subgroups
329      if (IsTrivial(GGd) or (IsCyclic(GGd) and IsPrimePowerInt(Size(GGd))))
330        and Length(MaximalNormalSubgroups(G))= 1 then
331        # size of MaximalNormalSubgroups is an upper bound to the number of
332        # components
333        return [ G ];
334      fi;
335    fi;
336
337    gs := [ ];
338    for N in MinNs do
339      g := First(GeneratorsOfGroup(N), g -> g<>One(N));
340      if g <> fail then
341        AddSetIfCanEasilySortElements(gs, g);
342      fi;
343    od;
344    # normal subgroup containing all minimal subgroups cannot have complement
345    Ns := Filtered(Ns, N -> not IsSubset(N, gs));
346
347    return DirectFactorsOfGroupFromList(G, Ns, MinNs);
348  end);
349
350InstallMethod(DirectFactorsOfGroup, "generic method", true,
351                        [ IsGroup ], 0,
352  function(G)
353
354    local Gd,       # G'
355          GGd,      # G/G'
356          C,        # Center(G)
357          D,        # Intersection(C, Gd)
358          Ns,       # list of normal subgroups
359          MinNs,    # list of minimal normal subgroups
360          gs,       # list containing one generator for each MinNs
361          g,        # group element
362          N,        # (possible) component of G, containing g
363          B;        # (possible) complement of G in N
364
365    # for abelian groups return the canonical decomposition
366    if IsTrivial(G) then
367      return [G];
368    elif IsAbelian(G) then
369      Ns := [];
370      for g in IndependentGeneratorsOfAbelianGroup(G) do
371        Ns := UnionIfCanEasilySortElements(Ns, [SubgroupNC(G, [g])]);
372      od;
373      return Ns;
374    fi;
375
376    if not IsFinite(G) then TryNextMethod(); fi;
377
378    # the KN method performs slower in practice, only called if forced
379    if ValueOption("useKN") = true then
380      return DirectFactorsOfGroupByKN(G);
381    fi;
382
383    # nilpotent groups are direct products of Sylow subgroups
384    if IsNilpotentGroup(G) then
385      if not IsPGroup(G) then
386        Ns := [ ];
387        for N in SylowSystem(G) do
388          Ns := UnionIfCanEasilySortElements(Ns, DirectFactorsOfGroup(N));
389        od;
390        return Ns;
391      elif IsCyclic(Center(G)) then
392        # G is direct indecomposable, because has a unique minimal subgroup
393        return [ G ];
394      fi;
395    # nonabelian p-groups cannot have a unique maximal subgroup
396    elif IsSolvableGroup(G) then
397      GGd := CommutatorFactorGroup(G);
398      if IsCyclic(GGd) and IsPrimePowerInt(Size(GGd)) then
399        # G is direct indecomposable, because has a unique maximal subgroup
400        return [ G ];
401      fi;
402    fi;
403
404    # look for abelian cyclic component from the center
405    C := Center(G);
406    # abelian cyclic components have trivial intersection with the commutator
407    Gd := DerivedSubgroup(G);
408    D := Intersection(C, Gd);
409    for g in RationalClasses(C) do
410      N := Subgroup(C, [Representative(g)]);
411      if not IsTrivial(N) and IsTrivialNormalIntersection(C, D, N) then
412        B := NormalComplementNC(G, N);
413        # if B is a complement to N
414        if B <> fail then
415          return UnionIfCanEasilySortElements( DirectFactorsOfGroup(N),
416                                                  DirectFactorsOfGroup(B));
417        fi;
418      fi;
419    od;
420
421    # all components are nonabelian
422    if IsCyclic(Gd) and IsPrimePowerInt(Size(Gd)) then
423      # if A and B are two nonabelian components, then
424      # A' and B' must be nontrivial
425      # this can only help for some metabelian groups
426      return [ G ];
427    fi;
428
429    if not IsTrivial(C) and HasAbelianFactorGroup(G, C)
430      and Length(DirectFactorsOfGroup(G/C)) < 4 then
431      # if A and B are two nonabelian components,
432      # where A/Center(A) and B/Center(B) are abelian, then
433      # A/Center(A) and B/Center(B) must have at least two components each
434      return [ G ];
435    fi;
436
437    # if everything else fails, compute all normal subgroups
438    Ns := NormalSubgroups(G);
439    if IsNilpotentGroup(G) then
440        # minimal normal subgroups are central in nilpotent groups
441        MinNs := MinimalNormalSubgroups(Center(G));
442    else
443      # MinimalNormalSubgroups seems to compute faster after NormalSubgroups
444      MinNs := MinimalNormalSubgroups(G);
445    fi;
446
447    if Length(MinNs)= 1 then
448      # size of MinNs is an upper bound to the number of components
449      return [ G ];
450    fi;
451
452    if not IsSolvableGroup(G) then
453      GGd := CommutatorFactorGroup(G);
454      # if GGd is not cyclic of prime power size then there are at least two
455      # maximal subgroups
456      if (IsTrivial(GGd) or (IsCyclic(GGd) and IsPrimePowerInt(Size(GGd))))
457        and Length(MaximalNormalSubgroups(G))= 1 then
458        # size of MaximalNormalSubgroups is an upper bound to the number of
459        # components
460        return [ G ];
461      fi;
462    fi;
463
464    gs := [ ];
465    for N in MinNs do
466      g := First(GeneratorsOfGroup(N), g -> g<>One(N));
467      if g <> fail then
468        AddSetIfCanEasilySortElements(gs, g);
469      fi;
470    od;
471    # normal subgroup containing all minimal subgroups cannot have complement
472    Ns := Filtered(Ns, N -> not IsSubset(N, gs));
473
474    return DirectFactorsOfGroupFromList(G, Ns, MinNs);
475  end);
476
477InstallMethod(CharacteristicFactorsOfGroup, "generic method", true,
478                        [ IsGroup ], 0,
479function(G)
480local Ns,MinNs,sel,a,sz,j,gs,g,N;
481
482  Ns := ShallowCopy(CharacteristicSubgroups(G));
483  SortBy(Ns,Size);
484  MinNs:=[];
485  sel:=[2..Length(Ns)-1];
486  while Length(sel)>0 do
487    a:=sel[1];
488    sz:=Size(Ns[a]);
489    RemoveSet(sel,sel[1]);
490    Add(MinNs,Ns[a]);
491    for j in ShallowCopy(sel) do
492      if Size(Ns[j])>sz and Size(Ns[j]) mod sz=0 and IsSubset(Ns[j],Ns[a]) then
493        RemoveSet(sel,j);
494      fi;
495    od;
496  od;
497
498  if Length(MinNs)= 1 then
499    # size of MinNs is an upper bound to the number of components
500    return [ G ];
501  fi;
502
503  gs := [ ];
504  for N in MinNs do
505    g := First(GeneratorsOfGroup(N), g -> g<>One(N));
506    if g <> fail then
507      AddSetIfCanEasilySortElements(gs, g);
508    fi;
509  od;
510  # normal subgroup containing all minimal subgroups cannot have complement
511  Ns := Filtered(Ns, N -> not IsSubset(N, gs));
512
513  return DirectFactorsOfGroupFromList(G, Ns, MinNs);
514end);
515
516InstallGlobalFunction( DirectFactorsOfGroupFromList,
517
518  function ( G, NList, MinList )
519
520    local g, N, gs, Ns, MinNs, NNs, MinNNs, facts, sizes, i, j, s1, s2;
521
522    if Length(MinList)=1 then
523      # size of MinList is an upper bound to the number of components
524      return [ G ];
525    fi;
526
527    Ns := ShallowCopy(NList);
528    MinNs := ShallowCopy(MinList);
529    gs := [ ];
530    for N in MinNs do
531      g := First(GeneratorsOfGroup(N), g -> g<>One(N));
532      if g <> fail then
533        AddSetIfCanEasilySortElements(gs, g);
534      fi;
535    od;
536    # normal subgroup containing all minimal subgroups cannot have complement
537    Ns := Filtered(Ns, N -> not IsSubset(N, gs));
538    sizes := List(Ns,Size);
539    SortParallel(sizes,Ns);
540    for s1 in Difference(Set(sizes),[Size(G),1]) do
541      i := PositionSet(sizes,s1);
542      s2 := Size(G)/s1;
543      if s1 <= s2 then
544        repeat
545          if s2 > s1 then
546            j := PositionSet(sizes,s2);
547            if j = fail then break; fi;
548          else
549            j := i + 1;
550          fi;
551          while j <= Size(sizes) and sizes[j] = s2 do
552            if IsTrivialNormalIntersectionInList(MinList,Ns[i],Ns[j]) then
553            # Ns[i] is the smallest direct factor, hence direct irreducible
554            # we keep from Ns only the subgroups of Ns[j] having size min. s1
555              NNs := Filtered(Ns{[i..j]}, N -> Size(N) >= s1
556                              and s2 mod Size(N) = 0 and IsSubset(Ns[j], N));
557              MinNNs := Filtered(MinNs, N -> s2 mod Size(N) = 0
558                                                and IsSubset(Ns[j], N));
559              return UnionIfCanEasilySortElements( [ Ns[i] ],
560                          DirectFactorsOfGroupFromList(Ns[j], NNs, MinNNs));
561            fi;
562            j := j + 1;
563          od;
564          i := i + 1;
565        until i>=Size(sizes) or sizes[i] <> s1;
566      fi;
567    od;
568    return [ G ];
569  end );
570
571InstallGlobalFunction(DirectFactorsOfGroupByKN,
572
573  function(G)
574
575    local Ns,       # list of some direct components
576          i,        # running index
577          Gd,       # derived subgroup G'
578          p,        # prime
579          N,        # (possible) component of G
580          B,        # complement of G in N
581          K,        # normal subgroup
582          prodK,    # product of normal subgroups
583          Z1,       # contains a (unique) component with trivial center
584          g,a,b,    # elements of G
585          C,        # Center(G)
586          D,        # Intersection(C, Gd)
587          Cl,       # all conjugacy classes of G
588          Clf,      # filtered list of conjugacy classes of G
589          c1,c2,c3, # conjugacy class of G
590          com,      # true if c1 and c2 commute, false otherwise
591          prod,     # product of c1 and c2
592          RedCl,    # reducible conjugacy classes of G
593          IrrCl,    # irreducible conjugacy classes of G
594          preedges, # pre-edges of the non-commuting graph
595          edges,    # final edges of the non-commuting graph
596          e,        # one edge of the non-commuting graph
597          comp,     # components of the non-commuting graph
598          DfZ1,     # nonabelian direct factors of Z1
599          S1;       # index set corresponding to first component
600
601    # for abelian groups return the canonical decomposition
602    if IsTrivial(G) then
603      return [G];
604    elif IsAbelian(G) then
605      Ns := [];
606      for g in IndependentGeneratorsOfAbelianGroup(G) do
607        Ns := UnionIfCanEasilySortElements(Ns, [SubgroupNC(G, [g])]);
608      od;
609      return Ns;
610    fi;
611
612    # look for abelian cyclic component from the center
613    C := Center(G);
614    # abelian cyclic components have trivial intersection with the commutator
615    Gd := DerivedSubgroup(G);
616    D := Intersection(C, Gd);
617    for g in RationalClasses(C) do
618      N := Subgroup(C, [Representative(g)]);
619      if not IsTrivial(N) and IsTrivialNormalIntersection(C, D, N) then
620        B := NormalComplementNC(G, N);
621        # if B is a complement to N
622        if B <> fail then
623          return UnionIfCanEasilySortElements( DirectFactorsOfGroup(N),
624                                                    DirectFactorsOfGroup(B));
625        fi;
626      fi;
627    od;
628
629    # all components are nonabelian
630    # !!! this can (and should) be made more efficient later !!!
631    # instead of conjugacy classes we should calculate by normal subgroups
632    # generated by 1 element
633    Cl := Set(ConjugacyClasses(G));
634    RedCl := Set(Filtered(Cl, x -> Size(x) = 1));
635    Clf := Difference(Cl, RedCl);
636    preedges := [];
637    for c1 in Clf do
638      for c2 in Clf do
639        com := true;
640        prod := [];
641        if c1 <> c2 then
642          for a in c1 do
643            for b in c2 do
644              g := a*b;
645              if g<>b*a then
646                com := false;
647                AddSetIfCanEasilySortElements(preedges, [c1, c2]);
648                break;
649              else
650                AddSetIfCanEasilySortElements(prod, g);
651              fi;
652            od;
653            if not com then
654              break;
655            fi;
656          od;
657          if com and Size(prod) = Size(c1)*Size(c2) then
658            a := Representative(c1);
659            b := Representative(c2);
660            c3 := ConjugacyClass(G, a*b);
661            if Size(c3)=Size(prod) then
662              AddSetIfCanEasilySortElements(RedCl, c3);
663            fi;
664          fi;
665        fi;
666      od;
667    od;
668    IrrCl := Difference(Clf, RedCl);
669
670    # need to remove edges corresponding to reducible classes
671    edges := ShallowCopy(preedges);
672    for e in preedges do
673      if Intersection(e, RedCl) <> [] then
674        RemoveSet(edges, e);
675      fi;
676    od;
677
678    # now we create the graph components of the irreducible class graph
679    # as an equivalence relation
680    # this is not compatible with and not using the grape package
681    comp := EquivalenceRelationPartition(
682              EquivalenceRelationByPairsNC(IrrCl, edges));
683
684    # replace classes in comp by their representatives
685    comp := List(comp, x-> Set(x, Representative));
686
687    # now replace every list by their generated normal subgroup
688    Ns := List(comp, x-> NormalClosure(G, SubgroupNC(G, x)));
689    Ns := UnionIfCanEasilySortElements(Ns);
690    if IsTrivial(C) or Size(Ns)=1 then
691      return Ns;
692    fi;
693
694    # look for the possible direct components from Ns
695    # partition to two sets, consider both
696    for S1 in IteratorOfCombinations(Ns) do
697      if S1 <> [] and S1<>Ns then
698        prodK := TrivialSubgroup(G);
699        for K in S1 do
700          prodK := ClosureGroup(prodK, K);
701        od;
702        Z1 := Centralizer(G, prodK);
703        # Z1 is nonabelian <==> contains a nonabelian factor
704        if not IsAbelian(Z1) then
705          N := First(DirectFactorsOfGroupByKN(Z1), x-> not IsAbelian(x));
706          # not sure if IsNormal(G, N) should be checked
707
708          # this certainly can be done better,
709          # because we basically know all possible normal subgroups
710          # but it suffices for now
711          B := NormalComplement(G, N);
712          if B <> fail then
713            # N is direct indecomposable by construction
714            return UnionIfCanEasilySortElements([N],
715                                                  DirectFactorsOfGroupByKN(B));
716          fi;
717        fi;
718      fi;
719    od;
720
721    # if no direct component is found
722    return [ G ];
723  end);
724
725
726#############################################################################
727##
728#M  SemidirectDecompositions( <G> ) . . . . . . . . . . . . .  generic method
729##
730InstallGlobalFunction(SemidirectDecompositionsOfFiniteGroup, function( arg )
731
732  local  G, Ns, fullNs, method, NHs, i, N, H, NH; #, sizes;
733
734  method := "all";
735  if Length(arg) = 1 and IsGroup(arg[1]) then
736    G := arg[1];
737    fullNs := true;
738  elif Length(arg) = 2 and IsGroup(arg[1]) and arg[2] in ["all",
739                                                          "any", "str"] then
740    G := arg[1];
741    method := arg[2];
742    fullNs := true;
743  elif Length(arg) = 2 and IsGroup(arg[1]) and IsList(arg[2])
744      and ForAll( Set(arg[2]), N ->
745                              IsSubgroup(arg[1], N) and IsNormal(arg[1], N))
746      then
747    G := arg[1];
748    Ns := ShallowCopy(arg[2]);
749    fullNs := false;
750  elif Length(arg) = 3 and IsGroup(arg[1]) and IsList(arg[2])
751      and ForAll( Set(arg[2]), N ->
752                              IsSubgroup(arg[1], N) and IsNormal(arg[1], N))
753      and arg[3] in ["all", "any", "str"] then
754    G := arg[1];
755    Ns := ShallowCopy(arg[2]);
756    method := arg[3];
757    fullNs := false;
758  else
759    Error("usage: SemidirectDecompositionsOfFiniteGroup(<G> [, <Ns>] [, <mthd>])");
760  fi;
761
762  if HasSemidirectDecompositions(G) then
763    NHs := [ ];
764    for NH in SemidirectDecompositions(G) do
765      N := NH[1];
766      H := NH[2];
767      if method in [ "any", "str" ] and not IsTrivial(N)
768        and not IsTrivial(H) and
769        ( not IsBound(Ns) or N in Ns ) then
770        return [ N, H ];
771      elif method="all" and
772        ( not IsBound(Ns) or N in Ns ) then
773        AddSetIfCanEasilySortElements(NHs, [ N, H ]);
774      fi;
775    od;
776    if method in [ "any", "str" ] then
777      return fail;
778    elif method = "all" then
779      return NHs;
780    fi;
781  fi;
782
783  if method in [ "any", "str" ] then
784    if HasSemidirectProductInfo(G) then
785      N := Image(Embedding(G, 2));
786      H := Image(Embedding(G, 1));
787      if not IsTrivial(N) and not IsTrivial(H) and
788        ( not IsBound(Ns) or N in Ns ) then
789        return [ N, H ];
790      fi;
791    fi;
792    N := NormalHallSubgroupsFromSylows(G, "any");
793    if N <> fail then
794      # by the Schur-Zassenhaus theorem there must exist a complement
795      if method = "any" then
796        H := ComplementClassesRepresentatives(G, N)[1];
797        return [ N, H ];
798      # only the isomorphism type of the complement is interesting
799      elif method = "str" then
800        Assert(1, Length( ComplementClassesRepresentatives(G, N) ) > 0);
801        return [ N, G/N ];
802      fi;
803    fi;
804  fi;
805
806  # simple groups have no nontrivial normal subgroups
807  if IsSimpleGroup(G) then
808    if method in [ "any", "str" ] then
809      return fail;
810    elif method = "all" then
811      return [ [TrivialSubgroup(G), G], [G, TrivialSubgroup(G)] ];
812    fi;
813  fi;
814
815  if not IsBound(Ns) then
816    Ns := ShallowCopy(NormalSubgroups(G));
817  fi;
818# does not seem to make things faster
819#  if method in [ "any", "str" ] then
820#    sizes := List(Ns, Size);
821#    SortParallel(sizes, Ns);
822#    Ns := Reversed(Ns);
823#  fi;
824
825  NHs := [ ];
826  for N in Ns do
827    if not IsSolvableGroup(N) and not HasSolvableFactorGroup(G, N) then
828      # compute subgroup lattice, currently no other method for complement
829      ConjugacyClassesSubgroups(G);;
830    fi;
831    H := ComplementClassesRepresentatives(G, N);
832    if Length(H)>0 then
833      if method in ["any", "str"] and not IsTrivial(N)
834                                  and not IsTrivial(H[1]) then
835        return [ N, H[1] ];
836      else
837        for i in [1..Length(H)] do
838          AddSetIfCanEasilySortElements( NHs, [ N, H[i] ] );
839        od;
840      fi;
841    fi;
842  od;
843  if method in [ "any", "str" ] then
844    # no nontrivial decompositions exist
845    if fullNs then
846      SetSemidirectDecompositions(G,
847                      [ [TrivialSubgroup(G), G], [G, TrivialSubgroup(G)] ]);
848    fi;
849    return fail;
850  else
851    if fullNs then
852      SetSemidirectDecompositions(G, NHs);
853    fi;
854    return NHs;
855  fi;
856end);
857
858InstallMethod( SemidirectDecompositions,
859               "generic method", true, [ IsGroup and IsFinite ], 0,
860
861  function( G )
862    return SemidirectDecompositionsOfFiniteGroup(G);
863  end );
864
865RedispatchOnCondition( SemidirectDecompositions, true,
866    [ IsGroup ],
867    [ IsFinite ], 0);
868
869#############################################################################
870##
871#M  DecompositionTypesOfGroup( <G> ) . . . . . . . . . . . . . generic method
872##
873InstallMethod( DecompositionTypesOfGroup,
874               "generic method", true, [ IsGroup ], 0,
875
876  function ( G )
877
878    local  AG, a,  # abelian invariants; an invariant
879           CS,     # conjugacy classes of non-(1-or-G) subgroups
880           H,      # a subgroup (possibly normal)
881           N,      # a normal subgroup
882           T,      # an isom. type
883           TH, tH, # isom. types for H, a type
884           TN, tN, # isom. types for N, a type
885           DTypes; # the decomposition types
886
887    if not IsFinite(G) then TryNextMethod(); fi;
888
889    DTypes := [ ];
890
891    # abelian special case
892    if IsAbelian(G) then
893      AG := AbelianInvariants(G);
894      if Length(AG) = 1 then DTypes := Set([AG[1]]); else
895        T := ["x"];
896        for a in AG do Add(T,a); od;
897        DTypes := Set([T]);
898      fi;
899      return DTypes;
900    fi;
901
902    # brute force enumeration
903    CS  := ConjugacyClassesSubgroups( G );
904    CS  := CS{[2..Length(CS)-1]};
905    for N in Filtered(List(Reversed(CS),Representative),
906                      N -> IsNormal(G,N)) do
907      for H in List(CS, Representative) do # Lemma1 (`SemidirectFactors...')
908        if    Size(H)*Size(N) = Size(G)
909          and IsTrivial(NormalIntersection(N,H))
910        then
911          # recursion (exponentially) on (semi-)factors
912          TH := DecompositionTypesOfGroup(H);
913          TN := DecompositionTypesOfGroup(N);
914          if IsNormal(G,H)
915          then
916            # non-trivial G = H x N
917            for tH in TH do
918              for tN in TN do
919                T := [ ];
920                if   IsList(tH) and tH[1] = "x"
921                then Append(T,tH{[2..Length(tH)]});
922                else Add(T,tH); fi;
923                if   IsList(tN) and tN[1] = "x"
924                then Append(T,tN{[2..Length(tN)]});
925                else Add(T,tN); fi;
926                Sort(T);
927                AddSet(DTypes,Concatenation(["x"],T));
928              od;
929            od;
930          else
931            # non-direct, non-trivial G = H semidirect N
932            for tH in TH do
933              for tN in TN do
934                AddSet(DTypes,[":",tH,tN]);
935              od;
936            od;
937          fi;
938        fi;
939      od;
940    od;
941
942    # default: a non-split extension
943    if Length(DTypes) = 0 then DTypes := Set([["non-split",Size(G)]]); fi;
944
945    return DTypes;
946  end );
947
948#############################################################################
949##
950#M  IsDihedralGroup( <G> ) . . . . . . . . . . . . . . . . . . generic method
951##
952
953DoComputeDihedralGenerators := function(G)
954    local  Zn, G1, T, n, t, s, i;
955
956    if Size(G) mod 2 <> 0 then return fail; fi;
957    n := Size(G)/2;
958
959    # find a normal subgroup of G of type Zn
960    if n mod 2 <> 0 then
961      # G = < s, t | s^n = t^2 = 1, s^t = s^-1 >
962      # ==> Comm(s, t) = s^-1 t s t = s^-2 ==> G' = < s^2 > = < s >
963      Zn := DerivedSubgroup(G);
964      if not ( IsCyclic(Zn) and Size(Zn) = n ) then return fail; fi;
965    else # n mod 2 = 0
966      # G = < s, t | s^n = t^2 = 1, s^t = s^-1 >
967      # ==> Comm(s, t) = s^-1 t s t = s^-2 ==> G' = < s^2 >
968      G1 := DerivedSubgroup(G);
969      if not ( IsCyclic(G1) and Size(G1) = n/2 ) then return fail; fi;
970      # G/G1 = {1*G1, t*G1, s*G1, t*s*G1}
971      T := RightTransversal(G,G1);
972      i := 1;
973      repeat
974        Zn := ClosureGroup(G1,T[i]);
975        i  := i + 1;
976      until i > 4 or ( IsCyclic(Zn) and Size(Zn) = n );
977      if not ( IsCyclic(Zn) and Size(Zn) = n ) then return fail; fi;
978    fi; # now Zn is normal in G and Zn = < s | s^n = 1 >
979
980    # choose t in G\Zn and check dihedral structure
981    repeat t := Random(G); until not t in Zn;
982    if not (Order(t) = 2 and ForAll(GeneratorsOfGroup(Zn),s->t*s*t*s=s^0))
983    then return fail; fi;
984
985    # choose generator s of Zn
986    repeat s := Random(Zn); until Order(s) = n;
987    return [t,s];
988end;
989
990InstallMethod( IsDihedralGroup,
991               "for a group",
992               true,
993               [ IsGroup and IsFinite ], 0,
994function(G)
995    local gens;
996
997    gens := DoComputeDihedralGenerators(G);
998    if gens = fail then
999        return false;
1000    else
1001        SetDihedralGenerators(G, gens);
1002    fi;
1003    return true;
1004end);
1005
1006InstallMethod( DihedralGenerators,
1007               "for a group",
1008               [ IsGroup and IsFinite ],
1009function(G)
1010    local gens;
1011
1012    gens := DoComputeDihedralGenerators(G);
1013    SetIsDihedralGroup(G, gens <> fail);
1014    if gens = fail then
1015        ErrorNoReturn("G is not a dihedral group");
1016    fi;
1017    return gens;
1018end);
1019
1020#############################################################################
1021##
1022#M  IsQuaternionGroup( <G> ) . . . . . . . . . . . . . . . . . generic method
1023##
1024DoComputeGeneralisedQuaternionGenerators := function(G)
1025    local  N,    # size of G
1026           k,    # ld(N)
1027           n,    # N/2
1028           G1,   # derived subgroup of G
1029           Zn,   # cyclic normal subgroup of index 2 in G
1030           T,    # transversal of G/G1
1031           t, s, # canonical generators of the quaternion group
1032           i;    # counter
1033
1034    N := Size(G);
1035    k := LogInt(N,2);
1036    if not( 2^k = N and k >= 3 ) then return fail; fi;
1037    n := N/2;
1038
1039    # G = <t, s | s^(2^k) = 1, t^2 = s^(2^k-1), s^t = s^-1>
1040    # ==> Comm(s, t) = s^-1 t s t = s^-2 ==> G' = < s^2 >
1041    G1 := DerivedSubgroup(G);
1042    if not ( IsCyclic(G1) and Size(G1) = n/2 ) then return fail; fi;
1043
1044    # find a normal subgroup of G of type Zn
1045    # G/G1 = {1*G1, t*G1, s*G1, t*s*G1}
1046    T := RightTransversal(G, G1);
1047    i := 1;
1048    repeat
1049      Zn := ClosureGroup(G1,T[i]);
1050      i  := i + 1;
1051    until i > 4 or ( IsCyclic(Zn) and Size(Zn) = n );
1052    if not ( IsCyclic(Zn) and Size(Zn) = n ) then return fail; fi;
1053
1054    # now Zn is normal in G and Zn = < s | s^n = 1 >
1055    # choose t in G\Zn and check quaternion structure
1056    repeat t := Random(G); until not t in Zn;
1057    if not (Order(t) = 4 and ForAll(GeneratorsOfGroup(Zn), s->s^t*s = s^0))
1058    then return fail; fi;
1059
1060    # choose generator s of Zn
1061    repeat s := Random(Zn); until Order(s) = n;
1062    return [t,s];
1063end;
1064
1065InstallMethod( IsGeneralisedQuaternionGroup,
1066               "for a group",
1067               true,
1068               [ IsGroup and IsFinite ],
1069               0,
1070function(G)
1071    local gens;
1072
1073    gens := DoComputeGeneralisedQuaternionGenerators(G);
1074    if gens = fail then
1075        return false;
1076    else
1077        SetGeneralisedQuaternionGenerators(G, gens);
1078    fi;
1079    return true;
1080end);
1081
1082InstallMethod( GeneralisedQuaternionGenerators,
1083               "for a group",
1084               [ IsGroup and IsFinite ],
1085function(G)
1086    local gens;
1087
1088    gens := DoComputeGeneralisedQuaternionGenerators(G);
1089    SetIsGeneralisedQuaternionGroup(G, gens <> fail);
1090    if gens = fail then
1091        ErrorNoReturn("G is not a generalised quaternion group");
1092    fi;
1093    return gens;
1094end);
1095#############################################################################
1096##
1097#M  IsQuasiDihedralGroup( <G> ) . . . . . . . . . . . . . . .  generic method
1098##
1099InstallMethod( IsQuasiDihedralGroup,
1100               "generic method", true, [ IsGroup ], 0,
1101
1102  function ( G )
1103
1104    local  N,    # size of G
1105           k,    # ld(N)
1106           n,    # N/2
1107           G1,   # derived subgroup of G
1108           Zn,   # cyclic normal subgroup of index 2 in G
1109           T,    # transversal of G/G1
1110           t, s, # canonical generators of the quasidihedral group
1111           i;    # counter
1112
1113    if not IsFinite(G) then TryNextMethod(); fi;
1114
1115    N := Size(G);
1116    k := LogInt(N, 2);
1117    if not( 2^k = N and k >= 4 ) then return false; fi;
1118    n := N/2;
1119
1120    # G = <t, s | s^(2^n) = t^2 = 1, s^t = s^(-1 + 2^(n-1))>.
1121    # ==> Comm(s, t) = s^-1 t s t = s^(-2+2^(n-1))
1122    # ==> G' = < s^(-2+2^(n-1)) >, |G'| = n/2.
1123    G1 := DerivedSubgroup(G);
1124    if not ( IsCyclic(G1) and Size(G1) = n/2 ) then return false; fi;
1125
1126    # find a normal subgroup of G of type Zn
1127    # G/G1 = {1*G1, t*G1, s*G1, t*s*G1}
1128    T := RightTransversal(G, G1);
1129    i := 1;
1130    repeat
1131      Zn := ClosureGroup(G1,T[i]);
1132      i  := i + 1;
1133    until i > 4 or ( IsCyclic(Zn) and Size(Zn) = n );
1134    if not ( IsCyclic(Zn) and Size(Zn) = n ) then return false; fi;
1135
1136    # now Zn is normal in G and Zn = < s | s^n = 1 >
1137    # now remain only the possibilities for the structure:
1138    #   dihedral, quaternion, quasidihedral
1139    repeat t := Random(G); until not t in Zn;
1140
1141    # detect cases: dihedral, quaternion
1142    if   ForAll(GeneratorsOfGroup(Zn), s -> s^t*s = s^0)
1143    then return false; fi;
1144
1145    # choose t in Zn of order 2
1146    repeat
1147      t := Random(G);
1148    until not( t in Zn and Order(t) = 2 ); # prob = 1/4
1149
1150    # choose generator s of Zn
1151    repeat s := Random(Zn); until Order(s) = n;
1152
1153    SetQuasiDihedralGenerators(G,[t,s]);
1154    return true;
1155  end );
1156
1157#############################################################################
1158##
1159#M  IsAlternatingGroup( <G> ) . . . . . . . . . . . . . . . .  generic method
1160##
1161##  This method additionally sets the attribute `AlternatingDegree' in case
1162##  <G> is isomorphic to a natural alternating group.
1163##
1164InstallMethod( IsAlternatingGroup,
1165               "generic method", true, [ IsGroup ], 0,
1166
1167  function ( G )
1168
1169    local  n, ids, info;
1170
1171    if not IsFinite(G) then TryNextMethod(); fi;
1172
1173    if IsNaturalAlternatingGroup(G) then return true;fi;
1174    if Size(G) < 60 then
1175      if Size(G) = 1 then
1176        SetAlternatingDegree(G,0); return true;
1177      elif Size(G) = 3 then
1178        SetAlternatingDegree(G,3); return true;
1179      elif Size(G) = 12 and IdGroup(G) = [ 12, 3 ] then
1180        SetAlternatingDegree(G,4); return true;
1181      else return false; fi;
1182    fi;
1183
1184    if not IsSimpleGroup(G) then return false; fi;
1185
1186    info := IsomorphismTypeInfoFiniteSimpleGroup(G);
1187    if   info.series = "A"
1188    then SetAlternatingDegree(G,info.parameter); return true;
1189    else return false; fi;
1190  end );
1191
1192#############################################################################
1193##
1194#M  AlternatingDegree( <G> ) generic method, dispatch to `IsAlternatingGroup'
1195##
1196InstallMethod( AlternatingDegree,
1197               "generic method, dispatch to `IsAlternatingGroup'",
1198               true, [ IsGroup ], 0,
1199
1200  function ( G )
1201    if not IsFinite(G) then TryNextMethod(); fi;
1202    if IsNaturalAlternatingGroup(G) then return DegreeAction(G); fi;
1203    if IsAlternatingGroup(G) then return AlternatingDegree(G);
1204                             else return fail; fi;
1205  end );
1206
1207#############################################################################
1208##
1209#M  IsNaturalAlternatingGroup( <G> ) . . . . . . .  for non-permutation group
1210##
1211InstallOtherMethod( IsNaturalAlternatingGroup, "for non-permutation group",
1212                    true, [ IsGroup ], 0,
1213
1214  function ( G )
1215    if not IsFinite(G) then TryNextMethod(); fi;
1216    if not IsPermGroup(G) then return false; else TryNextMethod(); fi;
1217  end );
1218
1219#############################################################################
1220##
1221#M  IsSymmetricGroup( <G> ) . . . . . . . . . . . . . . . . .  generic method
1222##
1223##  This method additionally sets the attribute `SymmetricDegree' in case
1224##  <G> is isomorphic to a natural symmetric group.
1225##
1226InstallMethod( IsSymmetricGroup,
1227               "generic method", true, [ IsGroup ], 0,
1228
1229  function ( G )
1230
1231    local  G1;
1232
1233    if IsNaturalSymmetricGroup(G) then return true;fi;
1234    if not IsFinite(G) then TryNextMethod(); fi;
1235
1236    # special treatment of small cases
1237    if Size(G)<=2 then SetSymmetricDegree(G,Size(G)); return true;
1238    elif Size(G)=6 and not IsAbelian(G) then
1239      SetSymmetricDegree(G,3);return true;
1240    fi;
1241
1242    G1 := DerivedSubgroup(G);
1243    if   not (IsAlternatingGroup(G1) and Index(G,G1) = 2)
1244      # this requires deg>=4
1245      or not IsTrivial(Centralizer(G,G1))
1246      or Size(G) = 720 and IdGroup(G) <> [ 720, 763 ]
1247    then return false; fi;
1248    SetSymmetricDegree(G,AlternatingDegree(G1));
1249    return true;
1250  end );
1251
1252#############################################################################
1253##
1254#M  IsNaturalSymmetricGroup( <G> ) . . . . . . . .  for non-permutation group
1255##
1256InstallOtherMethod( IsNaturalSymmetricGroup, "for non-permutation group",
1257                    true, [ IsGroup ], 0,
1258
1259  function ( G )
1260    if not IsFinite(G) then TryNextMethod(); fi;
1261    if not IsPermGroup(G) then return false; else TryNextMethod(); fi;
1262  end );
1263
1264#############################################################################
1265##
1266#M  SymmetricDegree( <G> ) . . generic method, dispatch to `IsSymmetricGroup'
1267##
1268InstallMethod( SymmetricDegree,
1269               "generic method, dispatch to `IsSymmetricGroup'",
1270               true, [ IsGroup ], 0,
1271
1272  function ( G )
1273    if not IsFinite(G) then TryNextMethod(); fi;
1274    if IsNaturalSymmetricGroup(G) then return DegreeAction(G); fi;
1275    if IsSymmetricGroup(G) then return SymmetricDegree(G);
1276                           else return fail; fi;
1277  end );
1278
1279#############################################################################
1280##
1281#F  SizeGL(  <n>, <q> )
1282##
1283InstallGlobalFunction( SizeGL,
1284
1285  function ( n, q )
1286
1287    local N, qn, k;
1288
1289    N  := 1;
1290    qn := q^n;
1291    for k in [0..n-1] do
1292      N := N * (qn - q^k);
1293    od;
1294    return N;
1295  end );
1296
1297#############################################################################
1298##
1299#F  SizeSL(  <n>, <q> )
1300##
1301InstallGlobalFunction( SizeSL,
1302
1303  function ( n, q )
1304
1305    local N, qn, k;
1306
1307    N  := 1;
1308    qn := q^n;
1309    for k in [0..n-1] do
1310      N := N * (qn - q^k);
1311    od;
1312    return N/(q - 1);
1313  end );
1314
1315#############################################################################
1316##
1317#F  SizePSL(  <n>, <q> )
1318##
1319InstallGlobalFunction( SizePSL,
1320
1321  function ( n, q )
1322
1323    local N, qn, k;
1324
1325    N  := 1;
1326    qn := q^n;
1327    for k in [0..n-1] do
1328      N := N * (qn - q^k);
1329    od;
1330    return N/((q - 1)*(Gcd(n, q - 1)));
1331  end );
1332
1333#############################################################################
1334##
1335#F  LinearGroupParameters(  <N>  )
1336##
1337InstallGlobalFunction( LinearGroupParameters,
1338
1339  function ( N )
1340
1341    local  npeGL,      # list of possible [n, p, e] for a GL
1342           npeSL,      # list of possible [n, p, e] for a SL
1343           npePSL,     # list of possible [n, p, e] for a PSL
1344           n, p, e,    # N = Size(GL(n, p^e))
1345           pe, p2, ep, # p^ep is maximal prime power divisor of N
1346           e2,         # a divisor of ep
1347           x, r, G;    # temporaries
1348
1349    if not IsPosInt(N) then Error("<N> must be positive integer"); fi;
1350
1351    # Formeln:
1352    # |GL(n, q)|  = Product(q^n - q^k : k in [0..n-1])
1353    # |SL(n, q)|  = |GL(n, q)| / (q - 1)
1354    # |PSL(n, q)| = |SL(n, q)| / gcd(n, q - 1)
1355    #   mit q = p^e f"ur p prim, e >= 1, n >= 1.
1356
1357    # Betrachte N = |GL(n,q)|. Dann gilt f"ur n >= 2
1358    #   (1) nu_p(N) = e * Binomial(n,2) und
1359    #   (2) (q - 1)^n teilt N.
1360    npeGL := [ ]; npeSL := [ ]; npePSL := [ ];
1361    if N = 1 then
1362      return rec( npeGL := npeGL, npeSL := npeSL, npePSL := npePSL );
1363    fi;
1364    for pe in Collected(Factors(N)) do
1365      p  := pe[1];
1366      ep := pe[2];
1367
1368      # find e, n such that (1) e*Binomial(n,2) = ep
1369      for e in DivisorsInt(ep) do
1370
1371        # find n such that Binomial(n, 2) = ep/e
1372        # <==> 8 ep/e + 1 = (2 n - 1)^2
1373        x := 8*ep/e + 1;
1374        r := RootInt(x, 2);
1375        if r^2 = x then
1376          n := (r + 1)/2;
1377
1378          # decide it
1379          G := SizeGL(n, p^e);
1380          if N = G then Add(npeGL,[n, p, e]); fi;
1381          if N = G/(p^e - 1) then Add(npeSL, [n, p, e]); fi;
1382          if N = G/((p^e - 1)*GcdInt(p^e - 1, n)) then
1383            Add(npePSL, [n, p, e]);
1384          fi;
1385        fi;
1386      od;
1387    od;
1388    return rec( npeGL := npeGL, npeSL := npeSL, npePSL := npePSL );
1389  end );
1390
1391#############################################################################
1392##
1393#M  IsPSL( <G> )
1394##
1395InstallMethod( IsPSL,
1396               "generic method for finite groups", true, [ IsGroup ], 0,
1397
1398  function ( G )
1399
1400    local  npes, npe;  # list of possible PSL-parameters
1401
1402    if not IsFinite(G) then TryNextMethod(); fi;
1403
1404    if Size(G)>12 and not IsSimpleGroup(G) then
1405      return false;
1406    fi;
1407
1408    # check if G has appropiate size
1409    npes := LinearGroupParameters(Size(G)).npePSL;
1410    if Length(npes) = 0 then return false; fi;
1411
1412    # more than one npe-triple should only
1413    # occur in the cases |G| in [60, 168, 20160]
1414    if   Length(npes) > 1 and not( Size(G) in [60, 168, 20160] )
1415    then Error("algebraic panic! propably npe does not work"); fi;
1416
1417    # set the parameters
1418    npe := npes[1];
1419
1420    # catch the cases:
1421    #   PSL(2, 2) ~= S3, PSL(2, 3) ~= A4,
1422    # in which the PSL is not simple
1423
1424    # PSL(2, 2)
1425    if npes[1] = [2, 2, 1] then
1426      if IsAbelian(G) then return false; fi;
1427      SetParametersOfGroupViewedAsPSL(G,npe); return true;
1428
1429    # PSL(2, 3)
1430    elif npes[1] = [2, 3, 1] then
1431      if Size(DerivedSubgroup(G)) <> 4 then return false; fi;
1432      SetParametersOfGroupViewedAsPSL(G,npe); return true;
1433
1434   # PSL(3, 4) / PSL(4, 2)
1435    elif npes = [ [ 4, 2, 1 ], [ 3, 2, 2 ] ] then
1436      if   IdGroup(SylowSubgroup(G,2)) = [64,138] then npe := npes[1];
1437      elif IdGroup(SylowSubgroup(G,2)) = [64,242] then npe := npes[2]; fi;
1438      SetParametersOfGroupViewedAsPSL(G,npe); return true;
1439
1440    # other cases
1441    else
1442      if not IsSimpleGroup(G) then return false; fi;
1443      SetParametersOfGroupViewedAsPSL(G,npe); return true;
1444    fi;
1445  end );
1446
1447#############################################################################
1448##
1449#M  PSLDegree( <G> ) . . . . . . . . . . . . generic method for finite groups
1450##
1451InstallMethod( PSLDegree,
1452               "generic method for finite groups", true, [ IsGroup ], 0,
1453
1454  function ( G )
1455    if not IsFinite(G) then TryNextMethod(); fi;
1456    if not IsPSL(G) then return fail; fi;
1457    return ParametersOfGroupViewedAsPSL(G)[1];
1458  end );
1459
1460#############################################################################
1461##
1462#M  PSLUnderlyingField( <G> ) . . . . . . .  generic method for finite groups
1463##
1464InstallMethod( PSLUnderlyingField,
1465               "generic method for finite groups", true, [ IsGroup ], 0,
1466
1467  function ( G )
1468    if not IsFinite(G) then TryNextMethod(); fi;
1469    if not IsPSL(G) then return fail; fi;
1470    return GF(ParametersOfGroupViewedAsPSL(G)[2]^ParametersOfGroupViewedAsPSL(G)[3]);
1471  end );
1472
1473#############################################################################
1474##
1475#M  IsSL( <G> ) . . . . . . . . . . . . . .  generic method for finite groups
1476##
1477InstallMethod( IsSL,
1478               "generic method for finite groups", true, [ IsGroup ], 0,
1479
1480  function ( G )
1481
1482    local  npes,  # list of possible SL-parameters
1483           C;     # centre of G
1484
1485    if not IsFinite(G) then TryNextMethod(); fi;
1486
1487    # check if G has appropiate size
1488    npes := LinearGroupParameters(Size(G)).npeSL;
1489    if Length(npes) = 0 then return false; fi;
1490
1491    # more than one npe-triple should never occur
1492    if Length(npes) > 1 then
1493      Error("algebraic panic! this should not occur");
1494    fi;
1495    npes := npes[1];
1496
1497    # catch the cases:
1498    #   SL(2, 2) ~= S3, SL(2, 3)
1499    # in which the corresponding FactorGroup PSL is not simple
1500
1501    # SL(2, 2)
1502    if npes = [2, 2, 1] then
1503      if IsAbelian(G) then return false; fi;
1504      SetParametersOfGroupViewedAsSL(G,npes); return true;
1505
1506    # SL(2, 3)
1507    elif npes = [2, 3, 1] then
1508      if Size(DerivedSubgroup(G)) <> 8 then return false; fi;
1509      SetParametersOfGroupViewedAsSL(G,npes); return true;
1510
1511    # other cases, in which the contained PSL is simple
1512    else
1513
1514      # calculate the centre C of G, which should have the
1515      # size gcd(n, p^e - 1), and if so, check if G/C (which
1516      # should be the corresponding PSL) is simple
1517      C := Centre(G);
1518      if   Size(C) <> Gcd(npes[1],npes[2]^npes[3] - 1)
1519        or not IsSimpleGroup(G/C)
1520        or Size(G)/2 in List(NormalSubgroups(G),Size)
1521      then return false; fi;
1522     if   IsomorphismGroups(G,SL(npes[1],npes[2]^npes[3])) = fail
1523     then return false; fi;
1524     SetParametersOfGroupViewedAsSL(G,npes); return true;
1525    fi;
1526  end );
1527
1528#############################################################################
1529##
1530#M  SLDegree( <G> ) . . . . . . . . . . . .  generic method for finite groups
1531##
1532InstallMethod( SLDegree,
1533               "generic method for finite groups", true, [ IsGroup ], 0,
1534
1535  function ( G )
1536    if not IsFinite(G) then TryNextMethod(); fi;
1537    if not IsSL(G) then return fail; fi;
1538    if   HasIsNaturalSL(G) and IsNaturalSL(G)
1539    then return DimensionOfMatrixGroup(G); fi;
1540    return ParametersOfGroupViewedAsSL(G)[1];
1541  end );
1542
1543#############################################################################
1544##
1545#M  SLUnderlyingField( <G> ) . . . . . . . . generic method for finite groups
1546##
1547InstallMethod( SLUnderlyingField,
1548               "generic method for finite groups", true, [ IsGroup ], 0,
1549
1550  function ( G )
1551    if not IsFinite(G) then TryNextMethod(); fi;
1552    if not IsSL(G) then return fail; fi;
1553    if   HasIsNaturalSL(G) and IsNaturalSL(G)
1554    then return FieldOfMatrixGroup(G); fi;
1555    return GF(ParametersOfGroupViewedAsSL(G)[2]^ParametersOfGroupViewedAsSL(G)[3]);
1556  end );
1557
1558#############################################################################
1559##
1560#M  IsGL( <G> )
1561##
1562InstallMethod( IsGL,
1563               "generic method for finite groups", true, [ IsGroup ], 0,
1564
1565  function ( G )
1566
1567    local  npes,  # list of possible GL-parameters
1568           G1,    # derived subgroup of G
1569           C1;    # centre of G1
1570
1571    if not IsFinite(G) then TryNextMethod(); fi;
1572
1573    # check if G has appropiate size
1574    npes := LinearGroupParameters(Size(G)).npeGL;
1575    if Length(npes) = 0 then return false; fi;
1576
1577    # more than one npe-triple should never occur
1578    if Length(npes) > 1 then
1579      Error("algebraic panic! this should not occur");
1580    fi;
1581    npes := npes[1];
1582
1583    # catch the cases:
1584    #   GL(2, 2) ~= S3, GL(2, 3)
1585    # in which the contained group PSL is not simple
1586
1587    # GL(2, 2)
1588    if npes = [2, 2, 1] then
1589      if IsAbelian(G) then return false; fi;
1590      SetParametersOfGroupViewedAsGL(G,npes); return true;
1591
1592    # GL(2, 3)
1593    elif npes = [2, 3, 1] then
1594      if IdGroup(G) <> [48,29] then return false; fi;
1595      SetParametersOfGroupViewedAsGL(G,npes); return true;
1596
1597    # other cases, in which contained PSL is simple
1598    else
1599
1600      # calculate the derived subgroup which should be the
1601      # corresponding SL of index p^e - 1
1602      G1 := DerivedSubgroup(G);
1603      if Index(G, G1) <> npes[2]^npes[3] - 1 then return false; fi;
1604
1605      # calculate the centre C1 of G1, which should have the
1606      # size gcd(n, p^e - 1), and if so, check if G1/C1
1607      # (which should be the corresponding PSL) is simple
1608      C1 := Centre(G1);
1609      if   Size(C1) <> Gcd(npes[1],npes[2]^npes[3] - 1)
1610        or not IsSimpleGroup(G1/C1)
1611      then return false; fi;
1612      if   IsomorphismGroups(G,GL(npes[1],npes[2]^npes[3])) = fail
1613      then return false; fi;
1614      SetParametersOfGroupViewedAsGL(G,npes); return true;
1615    fi;
1616  end );
1617
1618#############################################################################
1619##
1620#M  GLDegree( <G> ) . . . . . . . . . . . .  generic method for finite groups
1621##
1622InstallMethod( GLDegree,
1623               "generic method for finite groups", true, [ IsGroup ], 0,
1624
1625  function ( G )
1626    if not IsFinite(G) then TryNextMethod(); fi;
1627    if not IsGL(G) then return fail; fi;
1628    if   HasIsNaturalGL(G) and IsNaturalGL(G)
1629    then return DimensionOfMatrixGroup(G); fi;
1630    return ParametersOfGroupViewedAsGL(G)[1];
1631  end );
1632
1633#############################################################################
1634##
1635#M  GLUnderlyingField( <G> ) . . . . . . . . generic method for finite groups
1636##
1637InstallMethod( GLUnderlyingField,
1638               "generic method for finite groups", true, [ IsGroup ], 0,
1639
1640  function ( G )
1641    if not IsFinite(G) then TryNextMethod(); fi;
1642    if not IsGL(G) then return fail; fi;
1643    if   HasIsNaturalGL(G) and IsNaturalGL(G)
1644    then return FieldOfMatrixGroup(G); fi;
1645    return GF(ParametersOfGroupViewedAsGL(G)[2]^ParametersOfGroupViewedAsGL(G)[3]);
1646  end );
1647
1648#############################################################################
1649##
1650#M  StructureDescription( <G> ) . . . . . . . . . for abelian or finite group
1651##
1652BindGlobal( "SD_insertsep", # function to join parts of name
1653    function ( strs, sep, brack )
1654
1655      local  short, s, i;
1656
1657      short := ValueOption("short") = true;
1658
1659      if strs = [] then return ""; fi;
1660      strs := Filtered(strs,str->str<>"");
1661      if Length(strs) > 1 then
1662        for i in [1..Length(strs)] do
1663          if   Intersection(strs[i],brack) <> ""
1664          then strs[i] := Concatenation("(",strs[i],")"); fi;
1665        od;
1666      fi;
1667      s := strs[1];
1668      for i in [2..Length(strs)] do
1669        s := Concatenation(s,sep,strs[i]);
1670      od;
1671      if short then RemoveCharacters(s," "); fi;
1672      return s;
1673    end);
1674
1675BindGlobal( "SD_cyclic",
1676    function ( n )
1677      if n = 0 or n = infinity then
1678        return "Z";
1679      fi;
1680      return Concatenation("C",String(n));
1681    end);
1682
1683BindGlobal( "SD_cycsaspowers", # function to write C2 x C2 x C2 as 2^3, etc.
1684    function ( name, cycsizes )
1685
1686      local  short, g, d, k, j, n;
1687
1688      short := ValueOption("short") = true;
1689      if not short then return name; fi;
1690      RemoveCharacters(name," ");
1691        cycsizes := Collected(cycsizes);
1692        for n in cycsizes do
1693          d := n[1]; k := n[2];
1694          g := SD_cyclic(d);
1695          if d = 0 then
1696            d := "Z";
1697          else
1698            d := String(d);
1699          fi;
1700          if k > 1 then
1701            for j in Reversed([2..k]) do
1702              name := ReplacedString(name,SD_insertsep(List([1..j],i->g),"x",""),
1703                        Concatenation(d,"^",String(j)));
1704            od;
1705          fi;
1706        od;
1707      RemoveCharacters(name,"C");
1708      return name;
1709    end);
1710
1711BindGlobal( "StructureDescriptionForAbelianGroups", # for abelian groups
1712
1713  function ( G )
1714
1715    local  cycsizes;     # sizes of cyclic factors of G
1716
1717    # special case trivial group
1718    if IsTrivial(G) then return "1"; fi;
1719
1720    # special case abelian group
1721    cycsizes := AbelianInvariants(G);
1722    cycsizes := Reversed(ElementaryDivisorsMat(DiagonalMat(cycsizes)));
1723    cycsizes := Filtered(cycsizes,n->n<>1);
1724    return SD_cycsaspowers(SD_insertsep(List(cycsizes, SD_cyclic),
1725                                        " x ",""), cycsizes);
1726  end );
1727
1728BindGlobal( "StructureDescriptionForFiniteSimpleGroups", # for simple groups
1729
1730  function ( G )
1731
1732    local  name,         # buffer for computed name
1733           series,       # series of simple groups
1734           parameter;    # parameters of G in series
1735
1736    # special case abelian group
1737    if IsAbelian(G) then return StructureDescriptionForAbelianGroups(G); fi;
1738
1739    # special case alternating group
1740    if   IsAlternatingGroup(G)
1741    then return Concatenation("A",String(AlternatingDegree(G))); fi;
1742
1743    # special case PSL
1744    if IsPSL(G) then
1745      return Concatenation("PSL(",String(PSLDegree(G)),",",
1746                                  String(Size(PSLUnderlyingField(G))),")");
1747    fi;
1748
1749    name := SplitString(IsomorphismTypeInfoFiniteSimpleGroup(G).name," ");
1750    name := name[1];
1751    if Position(name,',') = fail then RemoveCharacters(name,"()"); else
1752      series    := IsomorphismTypeInfoFiniteSimpleGroup(G).series;
1753      parameter := IsomorphismTypeInfoFiniteSimpleGroup(G).parameter;
1754      if   series = "2A" then
1755        name := Concatenation("PSU(",String(parameter[1]+1),",",
1756                                     String(parameter[2]),")");
1757      elif series = "B" then
1758        name := Concatenation("O(",String(2*parameter[1]+1),",",
1759                                   String(parameter[2]),")");
1760      elif series = "2B" then
1761        name := Concatenation("Sz(",String(parameter),")");
1762      elif series = "C" then
1763        name := Concatenation("PSp(",String(2*parameter[1]),",",
1764                                     String(parameter[2]),")");
1765      elif series = "D" then
1766        name := Concatenation("O+(",String(2*parameter[1]),",",
1767                                    String(parameter[2]),")");
1768      elif series = "2D" then
1769        name := Concatenation("O-(",String(2*parameter[1]),",",
1770                                    String(parameter[2]),")");
1771      elif series = "3D" then
1772        name := Concatenation("3D(4,",String(parameter),")");
1773      elif series in ["2F","2G"] and parameter > 2 then
1774        name := Concatenation("Ree(",String(parameter),")");
1775      fi;
1776    fi;
1777    return name;
1778  end );
1779
1780BindGlobal( "StructureDescriptionForFiniteGroups", # for finite groups
1781
1782  function ( G )
1783
1784    local  G1,           # the group G reconstructed; result
1785           Hs,           # split factors of G
1786           Gs,           # factors of G
1787           cyclics,      # cyclic factors of G
1788           cycsizes,     # sizes of cyclic factors of G
1789           noncyclics,   # noncyclic factors of G
1790           cycname,      # part of name corresponding to cyclics
1791           noncycname,   # part of name corresponding to noncyclics
1792           name,         # buffer for computed name
1793           cname,        # name for centre of G
1794           dname,        # name for derived subgroup of G
1795           NH, H, N, N1, # semidirect factors of G
1796           NHs,          # [N, H] decompositions
1797           NHname,       # name of NH
1798           NHs1,         # NH's with preferred N and H
1799           NHs1Names,    # names of products in NHs1
1800           len,          # maximal number of direct factors
1801           g,            # an element of G
1802           id,           # id of G in the library of perfect groups
1803           short,        # short / long output format
1804           nice,         # nice output (slower)
1805           i,j,          # counters
1806           primes,       # prime divisors of Size(G)
1807           d,            # divisor of Size(G)
1808           k,            # maximal power of d in Size(G)
1809           pi;           # subset of primes
1810
1811    short := ValueOption("short") = true;
1812    nice := ValueOption("nice") = true;
1813
1814    # fetch name from precomputed list, if available
1815    if ValueOption("recompute") <> true and Size(G) <= 2000 then
1816      if IsBound(NAMES_OF_SMALL_GROUPS[Size(G)]) then
1817        i := IdGroup(G)[2];
1818        if IsBound(NAMES_OF_SMALL_GROUPS[Size(G)][i]) then
1819          name := ShallowCopy(NAMES_OF_SMALL_GROUPS[Size(G)][i]);
1820          cycsizes := [];
1821          if short then
1822          # DivisorsInt is rather slow, but we only call it for small groups
1823            for d in Reversed(DivisorsInt(Size(G))) do
1824              if d >1 then
1825                k := LogInt(Size(G), d);
1826                if k>1 then
1827                  for j in [1..k] do
1828                    Add(cycsizes, d);
1829                  od;
1830                fi;
1831              fi;
1832            od;
1833          fi;
1834          return SD_cycsaspowers(name, cycsizes);
1835        fi;
1836      fi;
1837    fi;
1838
1839    # special case trivial group
1840    if IsTrivial(G) then return "1"; fi;
1841
1842    # special case abelian group
1843    if IsAbelian(G) then return StructureDescriptionForAbelianGroups(G); fi;
1844
1845    # special case alternating group
1846    if   IsAlternatingGroup(G)
1847    then return Concatenation("A",String(AlternatingDegree(G))); fi;
1848
1849    # special case symmetric group
1850    if   IsSymmetricGroup(G)
1851    then return Concatenation("S",String(SymmetricDegree(G))); fi;
1852
1853    # special case dihedral group
1854    if   IsDihedralGroup(G) and Size(G) > 6
1855    then return Concatenation("D",String(Size(G))); fi;
1856
1857    # special case quaternion group
1858    if   IsQuaternionGroup(G)
1859    then return Concatenation("Q",String(Size(G))); fi;
1860
1861    # special case quasidihedral group
1862    if   IsQuasiDihedralGroup(G)
1863    then return Concatenation("QD",String(Size(G))); fi;
1864
1865    # special case PSL
1866    if IsPSL(G) then
1867      return Concatenation("PSL(",String(PSLDegree(G)),",",
1868                                  String(Size(PSLUnderlyingField(G))),")");
1869    fi;
1870
1871    # special case SL
1872    if IsSL(G) then
1873      return Concatenation("SL(",String(SLDegree(G)),",",
1874                                 String(Size(SLUnderlyingField(G))),")");
1875    fi;
1876
1877    # special case GL
1878    if IsGL(G) then
1879      return Concatenation("GL(",String(GLDegree(G)),",",
1880                                 String(Size(GLUnderlyingField(G))),")");
1881    fi;
1882
1883    # other simple group
1884    if IsSimpleGroup(G) then
1885      return StructureDescriptionForFiniteSimpleGroups(G);
1886    fi;
1887
1888    # direct product decomposition
1889    Gs := DirectFactorsOfGroup( G );
1890    if Length(Gs) > 1 then
1891
1892      # decompose the factors
1893      Hs := List(Gs,StructureDescription);
1894
1895      # construct
1896      cyclics  := Filtered(Gs,IsCyclic);
1897      if cyclics <> [] then
1898        cycsizes := ElementaryDivisorsMat(DiagonalMat(List(cyclics,Size)));
1899        cycsizes := Filtered(cycsizes,n->n<>1);
1900        cycname  := SD_cycsaspowers(SD_insertsep(List(cycsizes, SD_cyclic),
1901                                    " x ",":."), cycsizes);
1902      else cycname := ""; fi;
1903      noncyclics := Difference(Gs,cyclics);
1904      noncycname := SD_insertsep(List(noncyclics,StructureDescription),
1905                                 " x ",":.");
1906
1907      return SD_insertsep([cycname,noncycname]," x ",":.");
1908    fi;
1909
1910    # semidirect product decomposition
1911    if not nice then
1912      NH := SemidirectDecompositionsOfFiniteGroup( G, "str" );
1913      if NH <> fail then
1914        H := NH[2]; N := NH[1];
1915        return SD_insertsep([StructureDescription(N),
1916                             StructureDescription(H)]," : ","x:.");
1917      fi;
1918    else
1919      NHs := [ ];
1920      for NH in SemidirectDecompositions( G ) do
1921        if not IsTrivial( NH[1] ) and not IsTrivial( NH[2] ) then
1922          AddSetIfCanEasilySortElements(NHs, [ NH[1], NH[2] ]);
1923        fi;
1924      od;
1925      if Length(NHs) > 0 then
1926
1927        # prefer abelian H; abelian N; many direct factors in N; phi injective
1928        NHs1 := Filtered(NHs, NH -> IsAbelian(NH[2]));
1929        if Length(NHs1) > 0 then NHs := NHs1; fi;
1930        NHs1 := Filtered(NHs, NH -> IsAbelian(NH[1]));
1931        if Length(NHs1) > 0 then
1932          NHs := NHs1;
1933          len := Maximum( List(NHs, NH -> Length(AbelianInvariants(NH[1]))) );
1934          NHs := Filtered(NHs, NH -> Length(AbelianInvariants(NH[1])) = len);
1935        fi;
1936        NHs1 := Filtered(NHs, NH -> Length(DirectFactorsOfGroup(NH[1])) > 1);
1937        if Length(NHs1) > 0 then
1938          NHs := NHs1;
1939          len := Maximum(List(NHs,NH -> Length(DirectFactorsOfGroup(NH[1]))));
1940          NHs := Filtered(NHs,NH -> Length(DirectFactorsOfGroup(NH[1]))=len);
1941        fi;
1942        NHs1 := Filtered(NHs, NH -> IsTrivial(Centralizer(NH[2],NH[1])));
1943        if Length(NHs1) > 0 then NHs := NHs1; fi;
1944        if Length(NHs) > 1 then
1945
1946          # decompose the pairs [N, H] and remove isomorphic copies
1947          NHs1      := [];
1948          NHs1Names := [];
1949          for NH in NHs do
1950            NHname := SD_insertsep([StructureDescription(NH[1]),
1951                                    StructureDescription(NH[2])],
1952                                                                " : ","x:.");
1953            if not NHname in NHs1Names then
1954              Add(NHs1,      NH);
1955              Add(NHs1Names, NHname);
1956            fi;
1957          od;
1958          NHs := NHs1;
1959
1960          if Length(NHs) > 1 then
1961            Info(InfoWarning,2,"Warning! Non-unique semidirect product:");
1962            Info(InfoWarning,2,List(NHs,NH -> List(NH,StructureDescription)));
1963          fi;
1964        fi;
1965
1966        H := NHs[1][2]; N := NHs[1][1];
1967
1968        return SD_insertsep([StructureDescription(N:nice),
1969                             StructureDescription(H:nice)]," : ","x:.");
1970      fi;
1971    fi;
1972
1973    # non-splitting, non-simple group
1974    if not IsTrivial(Centre(G)) then
1975      cname := SD_insertsep([StructureDescription(Centre(G)),
1976                             StructureDescription(G/Centre(G))]," . ","x:.");
1977    fi;
1978    if not IsPerfectGroup(G) then
1979      dname := SD_insertsep([StructureDescription(DerivedSubgroup(G)),
1980                             StructureDescription(G/DerivedSubgroup(G))],
1981                            " . ","x:.");
1982    fi;
1983    if   IsBound(cname) and IsBound(dname) and cname <> dname
1984    then return Concatenation(cname," = ",dname);
1985    elif IsBound(cname) then return cname;
1986    elif IsBound(dname) then return dname;
1987    elif not IsTrivial(FrattiniSubgroup(G))
1988    then return SD_insertsep([StructureDescription(FrattiniSubgroup(G)),
1989                              StructureDescription(G/FrattiniSubgroup(G))],
1990                             " . ","x:.");
1991    elif     IsPosInt(NrPerfectGroups(Size(G)))
1992         and not Size(G) in [ 86016, 368640, 737280 ]
1993    # this does not happen for Size(G)<10^6
1994    then
1995         id := PerfectIdentification(G);
1996         return Concatenation("PerfectGroup(",String(id[1]),",",
1997                                              String(id[2]),")");
1998    else return Concatenation("<a non-simple perfect group of order ",
1999                Size(G)," with trivial centre and trivial Frattini ",
2000                "subgroup, which cannot be written as a direct or ",
2001                "semidirect product of smaller groups>");
2002    fi;
2003  end );
2004
2005InstallMethod( StructureDescription, "for abelian groups",
2006               true, [ IsGroup and IsAbelian ], 0,
2007               StructureDescriptionForAbelianGroups );
2008
2009RedispatchOnCondition( StructureDescription, true,
2010    [ IsGroup ],
2011    [ IsAbelian ], 0);
2012
2013InstallMethod( StructureDescription, "for finite simple groups",
2014               true, [ IsGroup and IsFinite and IsSimpleGroup ], 0,
2015               StructureDescriptionForFiniteSimpleGroups );
2016
2017InstallMethod( StructureDescription, "for finite groups",
2018               true, [ IsGroup and IsFinite ], 0,
2019               StructureDescriptionForFiniteGroups );
2020
2021RedispatchOnCondition( StructureDescription, true,
2022    [ IsGroup ],
2023    [ IsFinite ], 0);
2024
2025#############################################################################
2026##
2027#M  ViewObj( <G> ) . . . . . . . . for group with known structure description
2028##
2029InstallMethod( ViewObj,
2030               "for groups with known structure description",
2031               true, [ IsGroup and HasStructureDescription ], SUM_FLAGS,
2032
2033  function ( G )
2034    if HasName(G) then TryNextMethod(); fi;
2035    Print(StructureDescription(G));
2036  end );
2037