Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/GAP/lib/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 18.9.2025 mit Größe 187 kB image not shown  

SSL grp.gi   Sprache: unbekannt

 
#############################################################################
##
##  This file is part of GAP, a system for computational discrete algebra.
##  This file's authors include Thomas Breuer, Frank Celler, Bettina Eick, Heiko Theißen.
##
##  Copyright of GAP belongs to its developers, whose names are too numerous
##  to list here. Please refer to the COPYRIGHT file for details.
##
##  SPDX-License-Identifier: GPL-2.0-or-later
##
##  This file contains generic methods for groups.
##


#############################################################################
##
#M  IsFinitelyGeneratedGroup( <G> ) . . test if a group is finitely generated
##
InstallImmediateMethod( IsFinitelyGeneratedGroup,
    IsGroup and HasGeneratorsOfGroup,
    function( G )
    if IsFinite( GeneratorsOfGroup( G ) ) then
      return true;
    fi;
    TryNextMethod();
    end );

#############################################################################
##
#M  IsCyclic( <G> ) . . . . . . . . . . . . . . . . test if a group is cyclic
##
#  This used to be an immediate method. It was replaced by an ordinary
#  method since the flag is typically set when creating the group.
InstallMethod( IsCyclic, true, [IsGroup and HasGeneratorsOfGroup], 0,
    function( G )
    if Length( GeneratorsOfGroup( G ) ) = 1 then
      return true;
    else
      TryNextMethod();
    fi;
    end );

InstallMethod( IsCyclic,
    "generic method for groups",
    [ IsGroup ],
    function ( G )
    local a;

    # if <G> has a generator list of length 1 then <G> is cyclic
    if HasGeneratorsOfGroup( G ) and Length( GeneratorsOfGroup(G) ) = 1 then
      a:=GeneratorsOfGroup(G)[1];
      if CanEasilyCompareElements(a) and not IsOne(a) then
        SetMinimalGeneratingSet(G,GeneratorsOfGroup(G));
      fi;
      return true;

    # if <G> is not commutative it is certainly not cyclic
    elif not IsCommutative( G )  then
        return false;

    # if <G> is finite, test if the <p>-th powers of the generators
    # generate a subgroup of index <p> for all prime divisors <p>
    elif IsFinite( G )  then
        return ForAll( PrimeDivisors( Size( G ) ),
                p -> Index( G, SubgroupNC( G,
                                 List( GeneratorsOfGroup( G ),g->g^p)) ) = p );

    # otherwise test if the abelian invariants are that of $Z$
    else
      return AbelianInvariants( G ) = [ 0 ];
    fi;
    end );

InstallMethod( Size,
    "for a cyclic group",
    [ IsGroup and IsCyclic and HasGeneratorsOfGroup and CanEasilyCompareElements ],
    {} -> -RankFilter(HasGeneratorsOfGroup),
function(G)
  local gens;
  if HasMinimalGeneratingSet(G) then
    gens:=MinimalGeneratingSet(G);
  else
    gens:=GeneratorsOfGroup(G);
  fi;
  if Length(gens) = 1 and gens[1] <> One(G) then
    SetMinimalGeneratingSet(G,gens);
    return Order(gens[1]);
  elif Length(gens) <= 1 then
    SetMinimalGeneratingSet(G,[]);
    return 1;
  fi;
  TryNextMethod();
end);

InstallMethod( MinimalGeneratingSet,"finite cyclic groups",true,
    [ IsGroup and IsCyclic and IsFinite ],
    {} -> RankFilter(IsFinite and IsPcGroup),
function ( G )
local g;
  if IsTrivial(G) then return []; fi;
  g:=Product(IndependentGeneratorsOfAbelianGroup(G),One(G));
  Assert( 1, Index(G,Subgroup(G,[g])) = 1 );
  return [g];
end);

#############################################################################
##
#M  MinimalGeneratingSet(<G>) . . . . . . . . . . . . . for groups
##
InstallMethod(MinimalGeneratingSet,"test solvable and 2-generator noncyclic",
  true, [IsGroup and IsFinite],0,
function(G)
  if not HasIsSolvableGroup(G) and IsSolvableGroup(G) and
     CanEasilyComputePcgs(G) then
    # discovered solvable -- redo
    return MinimalGeneratingSet(G);
  elif not IsSolvableGroup(G) then
    if IsGroup(G) and (not IsCyclic(G)) and HasGeneratorsOfGroup(G)
        and Length(GeneratorsOfGroup(G)) = 2 then
      return GeneratorsOfGroup(G);
    fi;
  fi;
  TryNextMethod();
end);

#############################################################################
##
#M  MinimalGeneratingSet(<G>)
##
InstallOtherMethod(MinimalGeneratingSet,"fallback method to inform user",true,
  [IsObject],0,
function(G)
  if IsGroup(G) and IsSolvableGroup(G) then
    TryNextMethod();
  else
    Error(
  "`MinimalGeneratingSet' currently assumes that the group is solvable, or\n",
  "already possesses a generating set of size 2.\n",
  "In general, try `SmallGeneratingSet' instead, which returns a generating\n",
  "set that is small but not of guaranteed smallest cardinality");
  fi;
end);

InstallOtherMethod(MinimalGeneratingSet,"finite groups",true,
  [IsGroup and IsFinite],0,
function(g)
local r,i,j,u,f,q,n,lim,sel,nat,ok,mi;
  if not HasIsSolvableGroup(g) and IsSolvableGroup(g) and
     CanEasilyComputePcgs(g) then
    return MinimalGeneratingSet(g);
  fi;
  # start at rank 2/abelian rank
  n:=AbelianInvariants(g);
  if Length(n)>0 then
    r:=Maximum(List(Set(List(n,SmallestPrimeDivisor)),
      x->Number(n,y->y mod x=0)));
  else r:=0; fi;
  r:=Maximum(r,2);
  n:=false;
  repeat
    if Length(GeneratorsOfGroup(g))=r then
      return GeneratorsOfGroup(g);
    fi;
    for i in [1..10^r] do
      u:=SubgroupNC(g,List([1..r],x->Random(g)));
      if Size(u)=Size(g) then return GeneratorsOfGroup(u);fi; # found
    od;
    f:=FreeGroup(r);
    ok:=false;
    if not IsSolvableGroup(g) then
      if n=false then
        n:=ShallowCopy(NormalSubgroups(g));
        if IsPerfectGroup(g) then
          # all perfect groups of order <15360 *are* 2-generated
          lim:=15360;
        else
          # all groups of order <8 *are* 2-generated
          lim:=8;
        fi;
        n:=Filtered(n,x->IndexNC(g,x)>=lim and Size(x)>1);
        SortBy(n,x->-Size(x));
        mi:=MinimalInclusionsGroups(n);
      fi;
      i:=1;
      while i<=Length(n) do
        ok:=false;
        # is factor randomly r-generated?
        q:=2^r;
        while ok=false and q>0 do
          u:=n[i];
          for j in [1..r] do
            u:=ClosureGroup(u,Random(g));
          od;
          ok:=Size(u)=Size(g);
          q:=q-1;
        od;

        if not ok then
          # is factor a nonsplit extension with minimal normal -- if so rank
          # stays the same, no new test

          # minimal overnormals
          sel:=List(Filtered(mi,x->x[1]=i),x->x[2]);
          if Length(sel)>0 then
            nat:=NaturalHomomorphismByNormalSubgroupNC(g,n[i]);
            for j in sel do
              if not ok then
                # nonsplit extension (so pre-images will still generate)?
                ok:=0=Length(
                  ComplementClassesRepresentatives(Image(nat),Image(nat,n[j])));
              fi;
            od;
          fi;
        fi;

        if not ok then
          q:=GQuotients(f,g/n[i]:findall:=false);
          if Length(q)=0 then
            # fail in quotient
            i:=Length(n)+10;
            Info(InfoGroup,2,"Rank ",r," fails in quotient\n");
          fi;
        fi;

        i:=i+1;
      od;

    fi;
    if n=false or i<=Length(n)+1 then
      # still try group
      q:=GQuotients(f,g:findall:=false);
      if Length(q)>0 then return List(GeneratorsOfGroup(f),
        x->ImagesRepresentative(q[1],x)) ;fi; # found

    fi;
    r:=r+1;
  until false;
end);


#############################################################################
##
#M  IsElementaryAbelian(<G>)  . . . . . test if a group is elementary abelian
##
InstallMethod( IsElementaryAbelian,
    "generic method for groups",
    [ IsGroup ],
    function ( G )
    local   i,          # loop
            p;          # order of one generator of <G>

    # if <G> is not commutative it is certainly not elementary abelian
    if not IsCommutative( G )  then
        return false;

    # if <G> is trivial it is certainly elementary abelian
    elif IsTrivial( G )  then
        return true;

    # if <G> is infinite it is certainly not elementary abelian
    elif HasIsFinite( G ) and not IsFinite( G )  then
        return false;

    # otherwise compute the order of the first nontrivial generator
    else
        # p := Order( GeneratorsOfGroup( G )[1] );
        i:=1;
        repeat
            p:=Order(GeneratorsOfGroup(G)[i]);
            i:=i+1;
        until p>1; # will work, as G is not trivial

        # if the order is not a prime <G> is certainly not elementary abelian
        if not IsPrime( p )  then
            return false;

        # otherwise test that all other nontrivial generators have order <p>
        else
            return ForAll( GeneratorsOfGroup( G ), gen -> gen^p = One( G ) );
        fi;

    fi;
    end );


#############################################################################
##
#M  IsPGroup( <G> ) . . . . . . . . . . . . . . . . .  is a group a p-group ?
##

# The following helper function makes use of the fact that for any given prime
# p, any (possibly infinite) nilpotent group G is a p-group if and only if any
# generating set of G consists of p-elements (i.e. elements whose order is a
# power of p). For finite G this is well-known. The general case follows from
# e.g. 5.2.6 in "A Course in the Theory of Groups" by Derek J.S. Robinson,
# since it holds in the case were G is abelian, and since being a p-group is
# a property inherited by quotients and extensions.
BindGlobal( "IS_PGROUP_FOR_NILPOTENT",
    function( G )
    local p, gen, ord;

    p := fail;
    for gen in GeneratorsOfGroup( G ) do
      ord := Order( gen );
      if ord = infinity then
        return false;
      elif ord > 1 then
        if p = fail then
          p := SmallestRootInt( ord );
          if not IsPrimeInt( p ) then
            return false;
          fi;
        else
          if ord <> p^PValuation( ord, p ) then
            return false;
          fi;
        fi;
      fi;
    od;
    if p = fail then
      return true;
    fi;

    SetPrimePGroup( G, p );
    return true;
    end);

# The following helper function uses the well-known fact that a finite group
# is a p-group if and only if its order is a prime power.
BindGlobal( "IS_PGROUP_FROM_SIZE",
    function( G )
    local s, p;

    s:= Size( G );
    if s = 1 then
      return true;
    elif s = infinity then
      return fail; # cannot say anything about infinite groups
    fi;
    p := SmallestRootInt( s );
    if not IsPrimeInt( p ) then
      return false;
    fi;

    SetPrimePGroup( G, p );
    return true;
    end);

InstallMethod( IsPGroup,
    "generic method (check order of the group or of generators if nilpotent)",
    [ IsGroup ],
    function( G )

    # We inspect orders of group generators if the group order is not yet
    # known *and* the group knows to be nilpotent or is abelian;
    # thus an `IsAbelian' test may be forced (which can be done via comparing
    # products of generators) but *not* an `IsNilpotent' test.
    if HasSize( G ) and IsFinite( G ) then
      return IS_PGROUP_FROM_SIZE( G );
    elif ( HasIsNilpotentGroup( G ) and IsNilpotentGroup( G ) )
             or IsAbelian( G ) then
      return IS_PGROUP_FOR_NILPOTENT( G );
    elif IsFinite( G ) then
      return IS_PGROUP_FROM_SIZE( G );
    fi;
    TryNextMethod();
    end );

InstallMethod( IsPGroup,
    "for nilpotent groups",
    [ IsGroup and IsNilpotentGroup ],
    function( G )

    if HasSize( G ) and IsFinite( G ) then
      return IS_PGROUP_FROM_SIZE( G );
    else
      return IS_PGROUP_FOR_NILPOTENT( G );
    fi;
    end );


#############################################################################
##
#M  IsPowerfulPGroup( <G> ) . . . . . . . . . . is a group a powerful p-group ?
##
InstallMethod( IsPowerfulPGroup,
    "use characterisation of powerful p-groups based on rank ",
    [ IsGroup and HasRankPGroup and HasComputedOmegas ],
     function( G )
    local p;
    if (IsTrivial(G)) then
      return true;
    else
      p:=PrimePGroup(G);
      # We use the less known characterisation of powerful p groups
      # for p>3 by Jon Gonzalez-Sanchez, Amaia Zugadi-Reizabal
      # can be found in 'A characterization of powerful p-groups'
      if (p>3) then
        return RankPGroup(G)=Log(Order(Omega(G,p)),p);
      else
        TryNextMethod();
      fi;
    fi;
    end);


InstallMethod( IsPowerfulPGroup,
    "generic method checks inclusion of commutator subgroup in agemo subgroup",
    [ IsGroup ],
     function( G )
    local p;
    if IsPGroup( G ) = false then
      return false;
    elif IsTrivial(G) then
      return true;

    else

      p:=PrimePGroup(G);
      if p = 2 then
        return IsSubgroup(Agemo(G,2,2),DerivedSubgroup( G ));
      else
        return IsSubgroup(Agemo(G,p), DerivedSubgroup( G ));
      fi;
    fi;
    end);


#############################################################################
##
#M  IsRegularPGroup( <G> ) . . . . . . . . . . is a group a regular p-group ?
##
InstallMethod( IsRegularPGroup,
    [ IsGroup ],
function( G )
local p, hom, reps, as, a, b, ap, bp, ab, ap_bp, ab_p, g, h, H, N;

  if not IsPGroup(G) then
    return false;
  fi;

  p:=PrimePGroup(G);
  if p = 2 then
    # see [Hup67, Satz III.10.3 a)]
    return IsAbelian(G);
  elif p = 3 and DerivedLength(G) > 2 then
    # see [Hup67, Satz III.10.3 b)]
    return false;
  elif Size(G) <= p^p then
    # see [Hal34, Corollary 14.14], [Hall, p. 183], [Hup67, Satz III.10.2 b)]
    return true;
  elif NilpotencyClassOfGroup(G) < p then
    # see [Hal34, Corollary 14.13], [Hall, p. 183], [Hup67, Satz III.10.2 a)]
    return true;
  elif IsCyclic(DerivedSubgroup(G)) then
    # see [Hup67, Satz III.10.2 c)]
    return true;
  elif Exponent(G) = p then
    # see [Hup67, Satz III.10.2 d)]
    return true;
  elif p = 3 and RankPGroup(G) = 2 then
    # see [Hup67, Satz 10.3 b)]: at this point we know that the derived
    # subgroup is not cyclic, hence G is not regular
    return false;
  elif Size(G) < p^p * Size(Agemo(G,p)) then
    # see [Hal36, Theorem 2.3], [Hup67, Satz III.10.13]
    return true;
  elif Index(DerivedSubgroup(G), Agemo(DerivedSubgroup(G),p)) < p^(p-1) then
    # see [Hal36, Theorem 2.3], [Hup67, Satz III.10.13]
    return true;
  fi;

  # We now use Proposition 2 from A. Mann, "Regular p-groups. II", 1972, DOI
  # 10.1007/BF02764891, which states: If N is a central elementary abelian
  # subgroup of order p^2, such that G/M is regular for all M with 1<M<N, then
  # G is regular. The reverse implication also holds as all sections of a
  # regular p-group are again regular.
  #
  # Such a subgroup exists if and only if the center of G is not cyclic.
  #
  # As a heuristic, we only apply this criterion if the index of the center in
  # G is not too small, as otherwise a brute force search is faster.
  #
  # Note: the book Y. Berkovich, "Groups of Prime Power Order, Volume 1", 2008
  # states a stronger version of this as Corollary 7.7, where it is basically
  # claimed that it suffices to check just two subgroups M of N. This result
  # is attributed to the above paper by Mann, but I can't find it in there,
  # and it also simply is wrong: for example, the direct product of
  # SmallGroup(3^5,22) and SmallGroup(3^5,22) has a center of order p^2 = 9,
  # which contains four subgroups M of order p = 3. For two of those the
  # corresponding quotient G/M is regular, and for the other two it is not.
  H := Center(G);
  if not IsCyclic(H) and Index(G, H) > 250 then
    if Size(H) = p^2 then
      N := H;
    else
      N := Group(Filtered(Pcgs(H), g -> Order(g) = p){[1,2]});
    fi;
    Assert(0, Size(N) = p^2);
    Assert(0, IsElementaryAbelian(N));
    reps := MinimalNormalSubgroups(N);
    Info( InfoGroup, 2, "IsRegularPGroup: using Mann criterion, |G| = ", Size(G),
       ", |reps| = ", Length(reps));
    return ForAll(reps, M -> IsRegularPGroup(G/M));
  fi;

  # Fallback to actually check the defining criterion, i.e.:
  # for all a,b in G, we must have that a^p*b^p/(a*b)^p in (<a,b>')^p

  # It suffices to pick 'a' among conjugacy class representatives.
  # Moreover, if 'a' is central then the criterion automatically holds.
  # For z,z'\in Z(G), the criterion holds for (a,b) iff it holds for (az,bz').
  # We thus choose 'a' among lifts of conjugacy class representatives in G/Z(G).
  hom := NaturalHomomorphismByNormalSubgroup(G, Center(G));
  reps := ConjugacyClasses(Image(hom));
  reps := List(reps, Representative);
  reps := Filtered(reps, g -> not IsOne(g));
  reps := List(reps, g -> PreImagesRepresentative(hom, g));

  as := List(reps, a -> [a,a^p]);

  for b in Image(hom) do
    b := PreImagesRepresentative(hom, b);
    bp := b^p;
    for a in as do
      ap := a[2]; a := a[1];
      # if a and b commute the regularity condition automatically holds
      ab := a*b;
      if ab = b*a then continue; fi;

      # regularity is also automatic if a^p * b^p = (a*b)^p
      ap_bp := ap * bp;
      ab_p := ab^p;
      if ap_bp = ab_p then continue; fi;

      # if the subgroup generated H by a and b is itself regular, we are also
      # done. However we don't use recursion here, as it is too expensive.
      # we just check the direct definition, with a twist to avoid Agemo
      g := ap_bp / ab_p;
      h := Comm(a,b)^p;
      # clearly h is in Agemo(DerivedSubgroup(Group([a,b])), p), so if g=h or
      # g=h^-1 then the regularity condition is satisfied
      if g = h or IsOne(g*h) then continue; fi;
      H := Subgroup(G, [a,b]);
      N := NormalClosure(H, [h]);
      # To follow the definition of regular precisely we should now check if g
      # is in A:=Agemo(DerivedSubgroup(H), p). Clearly h=[a,b]^p and all its
      # conjugates are contained in A, and so N is a subgroup of A. But it
      # could be a *proper* subgroup. Alas, if G is regular, then also H is
      # regular, and from [Hup67, Hauptsatz III.10.5.b)] we conclude A = N and
      # the test g in N will succeed. If on the other hand G is not regular,
      # then H may also be not regular, and then N might be too small. But
      # that is fine (and even beneficial), because that just means we might
      # reach the 'return false' faster.
      if not g in N then
        return false;
      fi;
    od;
  od;
  return true;

end);


#############################################################################
##
#M  PrimePGroup . . . . . . . . . . . . . . . . . . . . .  prime of a p-group
##
InstallMethod( PrimePGroup,
    "generic method, check the order of a nontrivial generator",
    [ IsPGroup and HasGeneratorsOfGroup ],
function( G )
local gen, s;
  if IsTrivial( G ) then
    return fail;
  fi;
  for gen in GeneratorsOfGroup( G ) do
    s := Order( gen );
    if s <> 1 then
      break;
    fi;
  od;
  return SmallestRootInt( s );
end );

InstallMethod( PrimePGroup,
    "generic method, check the group order",
    [ IsPGroup ],
function( G )
local s;
  # alas, the size method might try to be really clever and ask for the size
  # again...
  if IsTrivial(G) then
    return fail;
  fi;
  s:= Size( G );
  if s = 1 then
    return fail;
  fi;
  return SmallestRootInt( s );
end );

RedispatchOnCondition (PrimePGroup, true,
    [IsGroup],
    [IsPGroup], 0);


#############################################################################
##
#M  IsNilpotentGroup( <G> ) . . . . . . . . . .  test if a group is nilpotent
##
#T InstallImmediateMethod( IsNilpotentGroup, IsGroup and HasSize, 10,
#T     function( G )
#T     G:= Size( G );
#T     if IsInt( G ) and IsPrimePowerInt( G ) then
#T       return true;
#T     fi;
#T     TryNextMethod();
#T     end );
#T This method does *not* fulfill the condition to be immediate,
#T factoring an integer may be expensive.
#T (Can we install a more restrictive method that *is* immediate,
#T for example one that checks only small integers?)

InstallMethod( IsNilpotentGroup,
    "if group size can be computed and is a prime power",
    [ IsGroup and CanComputeSize ], 25,
    function ( G )
    local s;

    s := Size ( G );
    if IsInt( s ) and IsPrimePowerInt( s ) then
        SetIsPGroup( G, true );
        SetPrimePGroup( G, SmallestRootInt( s ) );
        return true;
    elif s = 1 then
        SetIsPGroup( G, true );
        return true;
    elif s <> infinity then
        SetIsPGroup( G, false );
    fi;
    TryNextMethod();
    end );


InstallMethod( IsNilpotentGroup,
    "generic method for groups",
    [ IsGroup ],
    function ( G )
    local   S;          # lower central series of <G>

    # compute the lower central series
    S := LowerCentralSeriesOfGroup( G );

    # <G> is nilpotent if the lower central series reaches the trivial group
    return IsTrivial( Last(S) );
    end );


#############################################################################
##
#M  IsPerfectGroup( <G> ) . . . . . . . . . . . .  test if a group is perfect
##
InstallImmediateMethod( IsPerfectGroup,
    IsGroup and HasIsAbelian and IsSimpleGroup,
    0,
    grp -> not IsAbelian( grp ) );

InstallMethod( IsPerfectGroup, "for groups having abelian invariants",
    [ IsGroup and HasAbelianInvariants ],
    grp -> Length( AbelianInvariants( grp ) ) = 0 );

InstallMethod( IsPerfectGroup,
    "method for finite groups",
    [ IsGroup and IsFinite ],
function(G)
  if not CanComputeIndex(G,DerivedSubgroup(G)) then
    TryNextMethod();
  fi;
  return Index( G, DerivedSubgroup( G ) ) = 1;
end);


InstallMethod( IsPerfectGroup, "generic method for groups",
    [ IsGroup ],
    G-> IsSubset(DerivedSubgroup(G),G));


#############################################################################
##
#M  IsSporadicSimpleGroup( <G> )
##
InstallMethod( IsSporadicSimpleGroup,
    "for a group",
    [ IsGroup ],
    G ->     IsFinite( G )
         and IsSimpleGroup( G )
         and IsomorphismTypeInfoFiniteSimpleGroup( G ).series = "Spor" );


#############################################################################
##
#M  IsSimpleGroup( <G> )  . . . . . . . . . . . . . test if a group is simple
##
InstallMethod( IsSimpleGroup,
    "generic method for groups",
    [ IsGroup ],
    function ( G )
    local   C,          # one conjugacy class of <G>
            g;          # representative of <C>

    if IsTrivial( G ) then
      return false;
    fi;

    # loop over the conjugacy classes
    for C  in ConjugacyClasses( G )  do
        g := Representative( C );
        if g <> One( G )
            and NormalClosure( G, SubgroupNC( G, [ g ] ) ) <> G
        then
            return false;
        fi;
    od;

    # all classes generate the full group
    return true;
    end );


#############################################################################
##
#P  IsAlmostSimpleGroup( <G> )
##
##  Since the outer automorphism groups of finite simple groups are solvable,
##  a finite group <A>G</A> is almost simple if and only if the last member
##  in the derived series of <A>G</A> is a simple group <M>S</M> (which is
##  then necessarily nonabelian) such that the centralizer of <M>S</M> in
##  <A>G</A> is trivial.
##
##  (We could detect whether the given group is an extension of a group of
##  prime order by some automorphisms, as follows.
##  If the derived series ends with the trivial group then take the previous
##  member of the series, and check whether it has prime order and is
##  self-centralizing.)
##
InstallMethod( IsAlmostSimpleGroup,
    "for a group",
    [ IsGroup ],
    function( G )
    local der;

    if IsAbelian( G ) then
      # Exclude simple groups of prime order.
      return false;
    elif IsSimpleGroup( G ) then
      # Nonabelian simple groups are almost simple.
      return true;
    elif not IsFinite( G ) then
      TryNextMethod();
    fi;

    der:= DerivedSeriesOfGroup( G );
    der:= Last(der);
    if IsTrivial( der ) then
      return false;
    fi;
    return IsSimpleGroup( der ) and IsTrivial( Centralizer( G, der ) );
    end );


#############################################################################
##
#P  IsQuasisimpleGroup( <G> )
##
InstallMethod( IsQuasisimpleGroup,
    "for a group",
    [ IsGroup ],
    G -> IsPerfectGroup( G ) and IsSimpleGroup( G / Centre( G ) ) );


#############################################################################
##
#M  IsSolvableGroup( <G> )  . . . . . . . . . . . test if a group is solvable
##
##  By the Feit–Thompson odd order theorem, every group of odd order is
##  solvable.
##
##  Now suppose G is a group of order 2m, with m odd. Let G act on itself from
##  the right, yielding a monomorphism \phi:G \to Sym(G). G contains an
##  involution h; then \phi(h) decomposes into a product of m disjoint
##  transpositions, hence sign(\phi(h)) = -1. Hence the kernel N of the
##  composition x \mapsto sign(\phi(x)) is a normal subgroup of G of index 2,
##  hence |N| = m.
##
##  By the odd order theorem, N is solvable, and so is G. Thus the order of
##  any non-solvable finite group is a multiple of 4.
##
##  By Burnside's theorem, every group of order p^a q^b is solvable. If a
##  group of such order is not already caught by the reasoning above, then
##  it must have order 2^a q^b with a>1.
##
InstallImmediateMethod( IsSolvableGroup, IsGroup and HasSize, 10,
    function( G )
    local size;
    size := Size( G );
    if IsInt( size ) and size mod 4 <> 0 then
      return true;
    fi;
    TryNextMethod();
    end );

InstallMethod( IsSolvableGroup,
    "if group size is known and is not divisible by 4 or p^a q^b",
    [ IsGroup and HasSize ], 25,
    function( G )
    local size;
    size := Size( G );
    if IsInt( size ) then
      if size mod 4 <> 0 then
        return true;
      else
        size := size/4;
        while size mod 2 = 0 do
          size := size/2;
        od;
        if size = 1 then
          SetIsPGroup( G, true );
          SetPrimePGroup( G, 2 );
          return true;
        elif IsPrimePowerInt( size ) then
          return true;
        fi;
      fi;
    fi;
    TryNextMethod();
    end );

InstallMethod( IsSolvableGroup,
    "generic method for groups",
    [ IsGroup ],
    function ( G )
    local   S,          # derived series of <G>
            isAbelian,  # true if <G> is abelian
            isSolvable; # true if <G> is solvable

    # compute the derived series of <G>
    S := DerivedSeriesOfGroup( G );

    # the group is solvable if the derived series reaches the trivial group
    isSolvable := IsTrivial( Last(S) );

    # set IsAbelian filter
    isAbelian := isSolvable and Length( S ) <= 2;
    Assert(3, IsAbelian(G) = isAbelian);
    SetIsAbelian(G, isAbelian);

    return isSolvable;
    end );


#############################################################################
##
#M  IsSupersolvableGroup( <G> ) . . . . . .  test if a group is supersolvable
##
##  Note that this method automatically sets `SupersolvableResiduum'.
##  Analogously, methods for `SupersolvableResiduum' should set
##  `IsSupersolvableGroup'.
##
InstallMethod( IsSupersolvableGroup,
    "generic method for groups",
    [ IsGroup ],
    function( G )
    if IsNilpotentGroup( G ) then
#T currently the nilpotency test is much cheaper than the test below,
#T so we force it!
      return true;
    fi;
    return IsTrivial( SupersolvableResiduum( G ) );
    end );


#############################################################################
##
#M  IsPolycyclicGroup( <G> )  . . . . . . . . . test if a group is polycyclic
##
InstallMethod( IsPolycyclicGroup,
               "generic method for groups", true, [ IsGroup ], 0,

  function ( G )

    local  d;

    if IsFinite(G) then return IsSolvableGroup(G); fi;
    if not IsSolvableGroup(G) then return false; fi;
    d := DerivedSeriesOfGroup(G);
    return ForAll([1..Length(d)-1],i->Index(d[i],d[i+1]) < infinity
                                   or IsFinitelyGeneratedGroup(d[i]/d[i+1]));
  end );


#############################################################################
##
#M  IsTrivial( <G> )  . . . . . . . . . . . . . .  test if a group is trivial
##
InstallMethod( IsTrivial,
    [ IsGroup ],
    G -> ForAll( GeneratorsOfGroup( G ), gen -> gen = One( G ) ) );


#############################################################################
##
#M  AbelianInvariants( <G> )  . . . . . . . . . abelian invariants of a group
##
InstallMethod( AbelianInvariants,
    "generic method for groups",
    [ IsGroup ],
    function ( G )
    local   H,  p,  l,  r,  i,  j,  gns,  inv,  ranks, g,  cmm;

    if not IsFinite(G)  then
        if HasIsCyclic(G) and IsCyclic(G) then
          return [ 0 ];
        fi;
        TryNextMethod();
    elif IsTrivial( G )  then
        return [];
    fi;

    gns := GeneratorsOfGroup( G );
    inv := [];
    # the parent of this will be G
    cmm := DerivedSubgroup(G);
    for p  in PrimeDivisors( Size( G ) )  do
        ranks := [];
        repeat
            H := cmm;
            for g  in gns  do
                #NC is safe
                H := ClosureSubgroupNC( H, g ^ p );
            od;
            r := Size(G) / Size(H);
            Info( InfoGroup, 2,
                  "AbelianInvariants: |<G>| = ", Size( G ),
                  ", |<H>| = ", Size( H ) );
            G   := H;
            gns := GeneratorsOfGroup( G );
            if r <> 1  then
                Add( ranks, Length(Factors(Integers,r)) );
            fi;
        until r = 1;
        Info( InfoGroup, 2,
              "AbelianInvariants: <ranks> = ", ranks );
        if 0 < Length(ranks)  then
            l := List( [ 1 .. ranks[1] ], x -> 1 );
            for i  in ranks  do
                for j  in [ 1 .. i ]  do
                    l[j] := l[j] * p;
                od;
            od;
            Append( inv, l );
        fi;
    od;

    Sort( inv );
    return inv;
    end );

InstallMethod( AbelianRank ,"generic method for groups", [ IsGroup ],0,
function(G)
local a,r;
  a:=AbelianInvariants(G);
  r:=Number(a,IsZero);
  a:=Filtered(a,x->not IsZero(x));
  if Length(a)=0 then return r; fi;
  a:=List(Set(a,SmallestRootInt),p->Number(a,x->x mod p=0));
  return r+Maximum(a);
end);


#############################################################################
##
#M  IsInfiniteAbelianizationGroup( <G> )
##
InstallMethod( IsInfiniteAbelianizationGroup,"generic method for groups",
[ IsGroup ], G->0 in AbelianInvariants(G));


#############################################################################
##
#M  AsGroup( <D> ) . . . . . . . . . . . . . . .  domain <D>, viewed as group
##
InstallMethod( AsGroup, [ IsGroup ], 100, IdFunc );

InstallMethod( AsGroup,
    "generic method for collections",
    [ IsCollection ],
    function( D )
    local M, gens, m, minv, G, L;

    if IsGroup( D ) then
      return D;
    fi;

    # Check that the elements in the collection form a nonempty semigroup.
    M:= AsMagma( D );
    if M = fail or not IsAssociative( M ) then
      return fail;
    fi;
    gens:= GeneratorsOfMagma( M );
    if IsEmpty( gens ) or not IsGeneratorsOfMagmaWithInverses( gens ) then
      return fail;
    fi;

    # Check that this semigroup contains the inverses of its generators.
    for m in gens do
      minv:= Inverse( m );
      if minv = fail or not minv in M then
        return fail;
      fi;
    od;

    D:= AsSSortedList( D );
    G:= TrivialSubgroup( GroupByGenerators( gens ) );
    L:= ShallowCopy( D );
    SubtractSet( L, AsSSortedList( G ) );
    while not IsEmpty(L)  do
        G := ClosureGroupDefault( G, L[1] );
        SubtractSet( L, AsSSortedList( G ) );
    od;
    if Length( AsList( G ) ) <> Length( D )  then
        return fail;
    fi;
    G := GroupByGenerators( GeneratorsOfGroup( G ), One( D[1] ) );
    SetAsSSortedList( G, D );
    SetIsFinite( G, true );
    SetSize( G, Length( D ) );

    # return the group
    return G;
    end );


#############################################################################
##
#M  ChiefSeries( <G> )  . . . . . . . .  delegate to `ChiefSeriesUnderAction'
##
InstallMethod( ChiefSeries,
    "method for a group (delegate to `ChiefSeriesUnderAction')",
    [ IsGroup ],
    G -> ChiefSeriesUnderAction( G, G ) );


#############################################################################
##
#M  RefinedSubnormalSeries( <ser>,<n> )
##
InstallGlobalFunction("RefinedSubnormalSeries",function(ser,sub)
local new,i,c;
  new:=[];
  i:=1;
  if not IsSubset(ser[1],sub) then
    sub:=Intersection(ser[1],sub);
  fi;
  while i<=Length(ser) and IsSubset(ser[i],sub) do
    Add(new,ser[i]);
    i:=i+1;
  od;
  while i<=Length(ser) and not IsSubset(sub,ser[i]) do
    c:=ClosureGroup(sub,ser[i]);
    if Size(Last(new))>Size(c) then
      Add(new,c);
    fi;
    if Size(Last(new))>Size(ser[i]) then
      Add(new,ser[i]);
    fi;
    sub:=Intersection(sub,ser[i]);
    i:=i+1;
  od;
  if Size(sub)<Size(Last(new)) and i<=Length(ser) and Size(sub)>Size(ser[i]) then
    Add(new,sub);
  fi;
  while i<=Length(ser) do
    Add(new,ser[i]);
    i:=i+1;
  od;
  Assert(1,ForAll([1..Length(new)-1],x->Size(new[x])<>Size(new[x+1])));
  return new;
end);



#############################################################################
##
#M  CommutatorFactorGroup( <G> )  . . . .  commutator factor group of a group
##
InstallMethod( CommutatorFactorGroup,
    "generic method for groups",
    [ IsGroup ],
    function( G )
    G:= FactorGroupNC( G, DerivedSubgroup( G ) );
    SetIsAbelian( G, true );
    return G;
    end );


############################################################################
##
#M MaximalAbelianQuotient(<group>)
##
InstallMethod(MaximalAbelianQuotient,
    "not fp group",
    [ IsGroup ],
    function( G )
    if IsSubgroupFpGroup( G ) then
      TryNextMethod();
    fi;
    return NaturalHomomorphismByNormalSubgroupNC(G,DerivedSubgroup(G));
#T Here we know that the image is abelian, and this information may be
#T useful later on.
#T However, the image group of the homomorphism may be not stored yet,
#T so we do not attempt to set the `IsAbelian' flag for it.
end );

#############################################################################
##
#M  CompositionSeries( <G> )  . . . . . . . . . . . composition series of <G>
##
InstallMethod( CompositionSeries,
    "using DerivedSubgroup",
    [ IsGroup and IsFinite ],
function( grp )
    local   der,  series,  i,  comp,  low,  elm,  pelm,  o,  p,  x,
            j,  qelm;

    # this only works for solvable groups
    if HasIsSolvableGroup(grp) and not IsSolvableGroup(grp)  then
        TryNextMethod();
    fi;
    der := DerivedSeriesOfGroup(grp);
    if not IsTrivial(Last(der))  then
        TryNextMethod();
    fi;

    # build up a series
    series := [ grp ];
    for i  in [ 1 .. Length(der)-1 ]  do
        comp := [];
        low  := der[i+1];
        while low <> der[i]  do
            repeat
                elm := Random(der[i]);
            until not elm in low;
            for pelm  in PrimePowerComponents(elm)  do
                o := Order(pelm);
                p := Factors(o)[1];
                x := LogInt(o,p);
                for j  in [ x-1, x-2 .. 0 ]  do
                    qelm := pelm ^ ( p^j );
                    if not qelm in low  then
                        Add( comp, low );
                        low:= ClosureGroup( low, qelm );
                    fi;
                od;
            od;
        od;
        Append( series, Reversed(comp) );
    od;

    return series;

end );

InstallMethod( CompositionSeries,
    "for simple group", true, [IsGroup and IsSimpleGroup], 100,
    S->[S,TrivialSubgroup(S)]);

InstallMethod(CompositionSeriesThrough,"intersection/union",IsElmsColls,
  [IsGroup and IsFinite,IsList],0,
function(G,normals)
local cs,i,j,pre,post,c,new,rev;
  cs:=CompositionSeries(G);
  # find normal subgroups not yet in
  normals:=Filtered(normals,x->not x in cs);
  # do we satisfy by sheer dumb luck?
  if Length(normals)=0 then return cs;fi;

  SortBy(normals,x->-Size(x));
  # check that this is a valid series
  Assert(0,ForAll([2..Length(normals)],i->IsSubset(normals[i-1],normals[i])));

  # Now move series through normals by closure/intersection
  for j in normals do
    # first in cs that does not contain j
    pre:=PositionProperty(cs,x->not IsSubset(x,j));
    # first contained in j.
    post:=PositionProperty(cs,x->Size(j)>=Size(x) and IsSubset(j,x));

    # if j is in the series, then pre>post. pre=post impossible
    if pre<post then
      # so from pre to post-1 needs to be changed
      new:=cs{[1..pre-1]};

      rev:=[j];
      i:=post-1;
      repeat
        if not IsSubset(Last(rev),cs[i]) then
          c:=ClosureGroup(cs[i],j);
          if Size(c)>Size(Last(rev)) then
            # proper down step
            Add(rev,c);
          fi;
        fi;
        i:=i-1;
        # at some point this must reach j, then no further step needed
      until Size(c)=Size(cs[pre-1]) or i<pre;
      Append(new,Filtered(Reversed(rev),x->Size(x)<Size(cs[pre-1])));

      i:=pre;
      repeat
        if not IsSubset(cs[i],Last(new)) then
          c:=Intersection(cs[i],j);
          if Size(c)<Size(Last(new)) then
            # proper down step
            Add(new,c);
          fi;
        fi;
        i:=i+1;
      until Size(c)=Size(cs[post]);
    fi;
    cs:=Concatenation(new,cs{[post+1..Length(cs)]});
  od;
  return cs;
end);


#############################################################################
##
#M  ConjugacyClasses( <G> )
##

#############################################################################
##
#M  ConjugacyClassesMaximalSubgroups( <G> )
##


##############################################################################
##
#M  DerivedLength( <G> ) . . . . . . . . . . . . . . derived length of a group
##
InstallMethod( DerivedLength,
    "generic method for groups",
    [ IsGroup ],
    G -> Length( DerivedSeriesOfGroup( G ) ) - 1 );


##############################################################################
##
#M  HirschLength( <G> ) . . . . .hirsch length of a polycyclic-by-finite group
##
InstallMethod( HirschLength,
    "generic method for finite groups",
    [ IsGroup and IsFinite ],
    G -> 0 );


#############################################################################
##
#M  DerivedSeriesOfGroup( <G> ) . . . . . . . . . . derived series of a group
##
InstallMethod( DerivedSeriesOfGroup,
    "generic method for groups",
    [ IsGroup ],
    function ( G )
    local   S,          # derived series of <G>, result
            lastS,      # last element of S
            D;          # derived subgroups

    # print out a warning for infinite groups
    if (HasIsFinite(G) and not IsFinite( G ))
      and not (HasIsPolycyclicGroup(G) and IsPolycyclicGroup( G )) then
      Info( InfoWarning, 1,
            "DerivedSeriesOfGroup: may not stop for infinite group <G>" );
    fi;

    # compute the series by repeated calling of `DerivedSubgroup'
    S := [ G ];
    lastS := G;
    Info( InfoGroup, 2, "DerivedSeriesOfGroup: step ", Length(S) );
    D := DerivedSubgroup( G );

    while
      (not HasIsTrivial(lastS) or
            not IsTrivial(lastS)) and
      (
        (not HasIsPerfectGroup(lastS) and
         not HasAbelianInvariants(lastS) and D <> lastS) or
        (HasIsPerfectGroup(lastS) and not IsPerfectGroup(lastS))
        or (HasAbelianInvariants(lastS)
                            and Length(AbelianInvariants(lastS)) > 0)
      ) do
        Add( S, D );
        lastS := D;
        Info( InfoGroup, 2, "DerivedSeriesOfGroup: step ", Length(S) );
        D := DerivedSubgroup( D );
    od;

    # set filters if the last term is known to be trivial
    if HasIsTrivial(lastS) and IsTrivial(lastS) then
      SetIsSolvableGroup(G, true);
      if Length(S) <=2 then
        Assert(3, IsAbelian(G));
        SetIsAbelian(G, true);
      fi;
    fi;

    # set IsAbelian filter if length of derived series is more than 2
    if Length(S) > 2 then
      Assert(3, not IsAbelian(G));
      SetIsAbelian(G, false);
    fi;

    # return the series when it becomes stable
    return S;
    end );

#############################################################################
##
#M  DerivedSubgroup( <G> )  . . . . . . . . . . . derived subgroup of a group
##
InstallMethod( DerivedSubgroup,
    "generic method for groups",
    [ IsGroup ],
    function ( G )
    local   D,          # derived subgroup of <G>, result
            gens,       # group generators of <G>
            i,  j,      # loops
            comm;       # commutator of two generators of <G>

    # find the subgroup generated by the commutators of the generators
    D := TrivialSubgroup( G );
    gens:= GeneratorsOfGroup( G );
    for i  in [ 2 .. Length( gens ) ]  do
        for j  in [ 1 .. i - 1 ]  do
            comm := Comm( gens[i], gens[j] );
            #NC is safe (init with Triv)
            D := ClosureSubgroupNC( D, comm );
        od;
    od;

    # return the normal closure of <D> in <G>
    D := NormalClosure( G, D );
    if D = G  then D := G;  fi;
    return D;
    end );

InstallMethod( DerivedSubgroup,
    "for a group that knows it is perfect",
    [ IsGroup and IsPerfectGroup ],
    SUM_FLAGS, # this is better than everything else
    IdFunc );

InstallMethod( DerivedSubgroup,
    "for a group that knows it is abelian",
    [ IsGroup and IsAbelian ],
    SUM_FLAGS, # this is better than everything else
    TrivialSubgroup );


##########################################################################
##
#M  DimensionsLoewyFactors( <G> )  . . . . . . dimension of the Loewy factors
##
InstallMethod( DimensionsLoewyFactors,
    "for a group (that must be a finite p-group)",
    [ IsGroup ],
    function( G )

    local   p,  J,  x,  P,  i,  s,  j;

    # <G> must be a p-group
    if not IsPGroup( G )  then
      Error( "<G> must be a p-group" );
    fi;

    # get the prime and the Jennings series
    p := PrimePGroup( G );
    J := JenningsSeries( G );

    # construct the Jennings polynomial over the rationals
    x := Indeterminate( Rationals );
    P := One( x );
    for i  in [ 1 .. Length(J)-1 ]  do
        s := Zero( x );
        for j  in [ 0 .. p-1 ]  do
            s := s + x^(j*i);
        od;
        P := P * s^LogInt( Index( J[i], J[i+1] ), p );
    od;

    # the coefficients are the dimension of the Loewy series
    return CoefficientsOfUnivariatePolynomial( P );
    end );


#############################################################################
##
#M  ElementaryAbelianSeries( <G> )  . .  elementary abelian series of a group
##
InstallOtherMethod( ElementaryAbelianSeries,
    "method for lists",
    [ IsList and IsFinite],
    function( G )

    local i, A, f;

    # if <G> is a list compute an elementary series through a given normal one
    if not IsSolvableGroup( G[1] )  then
      return fail;
    fi;
    for i  in [ 1 .. Length(G)-1 ]  do
      if not IsNormal(G[1],G[i+1]) or not IsSubgroup(G[i],G[i+1])  then
        Error( "<G> must be normal series" );
      fi;
    od;

    # convert all groups in that list
    f := IsomorphismPcGroup( G[ 1 ] );
    A := ElementaryAbelianSeries(List(G,x->Image(f,x)));

    # convert back into <G>
    return List( A, x -> PreImage( f, x ) );
    end );

InstallMethod( ElementaryAbelianSeries,
    "generic method for finite groups",
    [ IsGroup and IsFinite],
    function( G )
    local f;

    # compute an elementary series if it is not known
    if not IsSolvableGroup( G )  then
      return fail;
    fi;

    # there is a method for pcgs computable groups we should use if
    # applicable, in this case redo
    if CanEasilyComputePcgs(G) then
      return ElementaryAbelianSeries(G);
    fi;

    f := IsomorphismPcGroup( G );

    # convert back into <G>
    return List( ElementaryAbelianSeries( Image( f )), x -> PreImage( f, x ) );
    end );

#############################################################################
##
#M  ElementaryAbelianSeries( <G> )  . .  elementary abelian series of a group
##
BindGlobal( "DoEASLS", function( S )
local   N,I,i,L;

  N:=ElementaryAbelianSeries(S);
  # remove spurious factors
  L:=[N[1]];
  I:=N[1];
  i:=2;
  repeat
    while i<Length(N) and HasElementaryAbelianFactorGroup(I,N[i+1])
      and (IsIdenticalObj(I,N[i]) or not N[i] in S) do
      i:=i+1;
    od;
    I:=N[i];
    Add(L,I);
  until Size(I)=1;

  # return it.
  return L;
end );

InstallMethod( ElementaryAbelianSeriesLargeSteps,
    "remove spurious factors", [ IsGroup ],
  DoEASLS);

InstallOtherMethod( ElementaryAbelianSeriesLargeSteps,
  "remove spurious factors", [IsList],
  DoEASLS);

#############################################################################
##
#M  Exponent( <G> ) . . . . . . . . . . . . . . . . . . . . . exponent of <G>
##
InstallMethod( Exponent,
    "generic method for finite groups",
    [ IsGroup and IsFinite ],
function(G)
  local exp, primes, p;
  exp := 1;
  primes := PrimeDivisors(Size(G));
  for p in primes do
    exp := exp * Exponent(SylowSubgroup(G, p));
  od;
  return exp;
end);

# ranked below the method for abelian groups
InstallMethod( Exponent,
    [ "IsGroup and IsFinite and HasConjugacyClasses" ],
    G-> Lcm(List(ConjugacyClasses(G), c-> Order(Representative(c)))) );

InstallMethod( Exponent,
    "method for finite abelian groups with generators",
    [ IsGroup and IsAbelian and HasGeneratorsOfGroup and IsFinite ],
    function( G )
    G:= GeneratorsOfGroup( G );
    if IsEmpty( G ) then
      return 1;
    fi;
    return Lcm( List( G, Order ) );
    end );

RedispatchOnCondition( Exponent, true, [IsGroup], [IsFinite], 0);

#############################################################################
##
#M  FittingSubgroup( <G> )  . . . . . . . . . . . Fitting subgroup of a group
##
InstallMethod( FittingSubgroup, "for nilpotent group",
    [ IsGroup and IsNilpotentGroup ], SUM_FLAGS, IdFunc );

InstallMethod( FittingSubgroup,
    "generic method for finite groups",
    [ IsGroup and IsFinite ],
    function (G)
        if not IsTrivial( G ) then
            G := SubgroupNC( G, Filtered(Union( List( PrimeDivisors( Size( G ) ),
                         p -> GeneratorsOfGroup( PCore( G, p ) ) ) ),
                         p->p<>One(G)));
            Assert( 2, IsNilpotentGroup( G ) );
            SetIsNilpotentGroup( G, true );
        fi;
        return G;
    end);

RedispatchOnCondition( FittingSubgroup, true, [IsGroup], [IsFinite], 0);


#############################################################################
##
#M  FrattiniSubgroup( <G> ) . . . . . . . . . .  Frattini subgroup of a group
##
InstallMethod( FrattiniSubgroup, "method for trivial groups",
            [ IsGroup and IsTrivial ], IdFunc );

InstallMethod( FrattiniSubgroup, "for abelian groups",
            [ IsGroup and IsAbelian ],
function(G)
    local i, abinv, indgen, p, q, gen;

    gen := [ ];
    abinv := AbelianInvariants(G);
    indgen := IndependentGeneratorsOfAbelianGroup(G);
    for i in [1..Length(abinv)] do
        q := abinv[i];
        if q<>0 and not IsPrime(q) then
            p := SmallestRootInt(q);
            Add(gen, indgen[i]^p);
        fi;
    od;
    return SubgroupNC(G, gen);
end);

InstallMethod( FrattiniSubgroup, "for powerful p-groups",
            [ IsPGroup and IsPowerfulPGroup and HasComputedAgemos ],100,
function(G)
    local p;
#If the group is powerful and has computed agemos, then no work needs
#to be done, since FrattiniSubgroup(G)=Agemo(G,p) in this case
#by properties of powerful p-groups.
        p:=PrimePGroup(G);
        return Agemo(G,p);
end);

InstallMethod( FrattiniSubgroup, "for nilpotent groups",
            [ IsGroup and IsNilpotentGroup ],
function(G)
    local hom, Gf;

    hom := MaximalAbelianQuotient(G);
    Gf := Image(hom);
    SetIsAbelian(Gf, true);
    return PreImage(hom, FrattiniSubgroup(Gf));
end);

InstallMethod( FrattiniSubgroup, "generic method for groups",
            [ IsGroup ],
            0,
function(G)
local m;
    if IsTrivial(G) then
      return G;
    fi;
    if not HasIsSolvableGroup(G) and IsSolvableGroup(G) then
       return FrattiniSubgroup(G);
    fi;
    m := List(ConjugacyClassesMaximalSubgroups(G),C->Core(G,Representative(C)));
    m := Intersection(m);
    if HasIsFinite(G) and IsFinite(G) then
      Assert(2,IsNilpotentGroup(m));
      SetIsNilpotentGroup(m,true);
    fi;
    return m;
end);


#############################################################################
##
#M  JenningsSeries( <G> ) . . . . . . . . . . .  jennings series of a p-group
##
InstallMethod( JenningsSeries,
    "generic method for groups",
    [ IsGroup ],
    function( G )

    local   p,  n,  i,  C,  L;

    # <G> must be a p-group
    if not IsPGroup( G ) then
        Error( "<G> must be a p-group" );
    fi;

    # get the prime
    p := PrimePGroup( G );

    # and compute the series
    # (this is a new variant thanks to Laurent Bartholdi)
    L := [ G ];
    n := 2;
    while not IsTrivial(L[n-1]) do
        L[n] := ClosureGroup(CommutatorSubgroup(G,L[n-1]),
            List(GeneratorsOfGroup(L[QuoInt(n+p-1,p)]),x->x^p));
        n := n+1;
    od;
    return L;

    end );


#############################################################################
##
#M  LowerCentralSeriesOfGroup( <G> )  . . . . lower central series of a group
##
InstallMethod( LowerCentralSeriesOfGroup,
    "generic method for groups",
    [ IsGroup ],
    function ( G )
    local   S,          # lower central series of <G>, result
            C;          # commutator subgroups

    # print out a warning for infinite groups
    if (HasIsFinite(G) and not IsFinite( G ))
      and not (HasIsNilpotentGroup(G) and IsNilpotentGroup( G )) then
      Info( InfoWarning, 1,
            "LowerCentralSeriesOfGroup: may not stop for infinite group <G>");
    fi;

    # compute the series by repeated calling of `CommutatorSubgroup'
    S := [ G ];
    Info( InfoGroup, 2, "LowerCentralSeriesOfGroup: step ", Length(S) );
    C := DerivedSubgroup( G );
    while C <> Last(S) do
        Add( S, C );
        Info( InfoGroup, 2, "LowerCentralSeriesOfGroup: step ", Length(S) );
        C := CommutatorSubgroup( G, C );
    od;

    # return the series when it becomes stable
    return S;
    end );

#############################################################################
##
#M  NilpotencyClassOfGroup( <G> )  . . . . lower central series of a group
##
InstallMethod(NilpotencyClassOfGroup,"generic",[IsGroup],0,
function(G)
  if not IsNilpotentGroup(G) then
    Error("<G> must be nilpotent");
  fi;
  return Length(LowerCentralSeriesOfGroup(G))-1;
end);

#############################################################################
##
#M  MaximalSubgroups( <G> )
##
InstallMethod(MaximalSubgroupClassReps,"default, catch dangerous options",
  true,[IsGroup],0,
function(G)
local a,m,i,l;
  # use ``try'' and set flags so that a known partial result is not used
  m:=TryMaximalSubgroupClassReps(G:
          cheap:=false,intersize:=false,inmax:=false,nolattice:=false);
  l:=[];
  for i in m do
    a:=SubgroupNC(G,GeneratorsOfGroup(i));
    if HasSize(i) then SetSize(a,Size(i));fi;
    Add(l,a);
  od;

  # now we know list is untained, store
  return l;

end);

# handle various options and flags
InstallGlobalFunction(TryMaximalSubgroupClassReps,
function(G)
local cheap,nolattice,intersize,attr,kill,i,flags,sup,sub,l;
  if HasMaximalSubgroupClassReps(G) then
    return MaximalSubgroupClassReps(G);
  fi;
  # the four possible options
  cheap:=ValueOption("cheap");
  if cheap=fail then cheap:=false;fi;
  nolattice:=ValueOption("nolattice");
  if nolattice=fail then nolattice:=false;fi;
  intersize:=ValueOption("intersize");
  if intersize=fail then intersize:=false;fi;
  #inmax:=ValueOption("inmax"); # should have no impact on validity of stored
  attr:=StoredPartialMaxSubs(G);
  # now find whether any stored information matches and which ones would be
  # superseded
  kill:=[];
  for i in [1..Length(attr)] do
    flags:=attr[i][1];
    # could use this stored result
    sup:=flags[3]=false or (IsInt(intersize) and intersize<=flags[3]);
    # would supersede the stored result
    sub:=intersize=false or (IsInt(flags[3]) and intersize>=flags[3]);
    sup:=sup and (cheap or not flags[1]);
    sub:=sub and (not cheap or flags[1]);
    sup:=sup and (nolattice or not flags[2]);
    sub:=sub and (not nolattice or flags[2]);
    if sup then return attr[i][2];fi; # use stored
    if sub then AddSet(kill,i);fi; # use stored
  od;
  l:=CalcMaximalSubgroupClassReps(G);
  Add(attr,Immutable([[cheap,nolattice,intersize],l]));
  # finally kill superseded ones (by replacing with last, which possibly was
  # just added)
  for i in Reversed(Set(kill)) do
    if i = Length(attr) then
      Remove(attr);
    else
      attr[i]:=Remove(attr);
    fi;
  od;
  return l;
end);

InstallMethod(StoredPartialMaxSubs,"set",true,[IsGroup],0,x->[]);

#############################################################################
##
#M  NrConjugacyClasses( <G> ) . . no. of conj. classes of elements in a group
##
InstallImmediateMethod( NrConjugacyClasses,
    IsGroup and HasConjugacyClasses and IsAttributeStoringRep,
    0,
    G -> Length( ConjugacyClasses( G ) ) );

InstallMethod( NrConjugacyClasses,
    "generic method for groups",
    [ IsGroup ],
    G -> Length( ConjugacyClasses( G ) ) );


#############################################################################
##
#A  IndependentGeneratorsOfAbelianGroup( <A> )
##
# to catch some trivial cases.
InstallMethod(IndependentGeneratorsOfAbelianGroup,"finite abelian group",
  true,[IsGroup and IsAbelian],0,
function(G)
local hom,gens;
  if not IsFinite(G) then
    TryNextMethod();
  fi;
  hom:=IsomorphismPermGroup(G);
  gens:=IndependentGeneratorsOfAbelianGroup(Image(hom,G));
  return List(gens,i->PreImagesRepresentative(hom,i));
end);


#############################################################################
##
#O  IndependentGeneratorExponents( <G>, <g> )
##
InstallMethod(IndependentGeneratorExponents,IsCollsElms,
  [IsGroup and IsAbelian, IsMultiplicativeElementWithInverse],0,
function(G,elm)
local ind, pcgs, primes, pos, p, i, e, f, a, j;
  if not IsBound(G!.indgenpcgs) then
    ind:=IndependentGeneratorsOfAbelianGroup(G);
    pcgs:=[];
    primes:=[];
    pos:=[];
    for i in ind do
      Assert(1, IsPrimePowerInt(Order(i)));
      p:=SmallestRootInt(Order(i));
      Add(primes,p);
      Add(pos,Length(pcgs)+1);
      while not IsOne(i) do
        Add(pcgs,i);
        i:=i^p;
      od;
    od;
    Add(pos,Length(pcgs)+1);
    pcgs:=PcgsByPcSequence(FamilyObj(One(G)),pcgs);
    G!.indgenpcgs:=rec(pcgs:=pcgs,primes:=primes,pos:=pos,gens:=ind);
  else
    pcgs:=G!.indgenpcgs.pcgs;
    primes:=G!.indgenpcgs.primes;
    pos:=G!.indgenpcgs.pos;
    ind:=G!.indgenpcgs.gens;
  fi;
  e:=ExponentsOfPcElement(pcgs,elm);
  f:=[];
  for i in [1..Length(ind)] do
    a:=0;
    for j in [pos[i+1]-1,pos[i+1]-2..pos[i]] do
      a:=a*primes[i]+e[j];
    od;
    Add(f,a);
  od;
  return f;
end);

#############################################################################
##
#M  Omega( <G>, <p>[, <n>] )  . . . . . . . . . . .  omega of a <p>-group <G>
##
InstallMethod( Omega,
    [ IsGroup, IsPosInt ],
    function( G, p )
    return Omega( G, p, 1 );
    end );

InstallMethod( Omega,
    [ IsGroup, IsPosInt, IsPosInt ],
    function( G, p, n )
    local known;

    # <G> must be a <p>-group
    if not IsPGroup(G) or PrimePGroup(G)<>p then
      Error( "Omega: <G> must be a p-group" );
    fi;

    known := ComputedOmegas( G );
    if not IsBound( known[ n ] )  then
        known[ n ] := OmegaOp( G, p, n );
    fi;
    return known[ n ];
    end );

InstallMethod( ComputedOmegas, [ IsGroup ], 0, G -> [  ] );


#############################################################################
##
#M  SolvableRadical( <G> )  . . . . . . . . . . . solvable radical of a group
##
InstallMethod( SolvableRadical,
  "factor out Fitting subgroup",
  [IsGroup and IsFinite],
function(G)
  local F,f;
  F := FittingSubgroup(G);
  if IsTrivial(F) then return F; fi;
  f := NaturalHomomorphismByNormalSubgroupNC(G,F);
  return PreImage(f,SolvableRadical(Image(f)));
end);

RedispatchOnCondition( SolvableRadical, true, [IsGroup], [IsFinite], 0);

InstallMethod( SolvableRadical,
    "solvable group is its own solvable radical",
    [ IsGroup and IsSolvableGroup ], 100,
    IdFunc );


#############################################################################
##
#M  GeneratorsSmallest( <G> ) . . . . . smallest generating system of a group
##
InstallMethod( GeneratorsSmallest,
    "generic method for groups",
    [ IsGroup ],
    function ( G )
    local   gens,       # smallest generating system of <G>, result
            gen,        # one generator of <gens>
            H;          # subgroup generated by <gens> so far

    # start with the empty generating system and the trivial subgroup
    gens := [];
    H := TrivialSubgroup( G );

    # loop over the elements of <G> in their order
    for gen  in EnumeratorSorted( G )  do

        # add the element not lying in the subgroup generated by the previous
        if not gen in H  then
            Add( gens, gen );
            #NC is safe (init with Triv)
            H := ClosureSubgroupNC( H, gen );

            # it is important to know when to stop
            if Size( H ) = Size( G )  then
                return gens;
            fi;

        fi;

    od;

    if Size(G)=1 then
      # trivial subgroup case
      return [];
    fi;

    # well we should never come here
    Error( "panic, <G> not generated by its elements" );
    end );

#############################################################################
##
#M  LargestElementGroup( <G> )
##
##  returns the largest element of <G> with respect to the ordering `\<' of
##  the elements family.
InstallMethod(LargestElementGroup,"use `EnumeratorSorted'",true,[IsGroup],
function(G)
  return EnumeratorSorted(G)[Size(G)];
end);


#############################################################################
##
#F  SupersolvableResiduumDefault( <G> ) . . . . supersolvable residuum of <G>
##
##  The algorithm constructs a descending series of normal subgroups with
##  supersolvable factor group from <G> to its supersolvable residuum such
##  that any subgroup that refines this series is normal in <G>.
##
##  In each step of the algorithm, a normal subgroup <N> of <G> with
##  supersolvable factor group is taken.
##  Then its commutator factor group is constructed and decomposed into its
##  Sylow subgroups.
##  For each, the Frattini factor group is considered as a <G>-module.
##  We are interested only in the submodules of codimension 1.
##  For these cases, the eigenspaces of the dual submodule are calculated,
##  and the preimages of their orthogonal spaces are used to construct new
##  normal subgroups with supersolvable factor groups.
##  If no eigenspace is found within one step, the residuum is reached.
##
##  The component `ds' describes a series such that any composition series
##  through `ds' from <G> down to the residuum is a chief series.
##
InstallGlobalFunction( SupersolvableResiduumDefault, function( G )
    local ssr,         # supersolvable residuum
          ds,          # component `ds' of the result
          gens,        # generators of `G'
          gs,          # small generating system of `G'
          p,           # loop variable
          o,           # group order
          size,        # size of `G'
          s,           # subgroup of `G'
          oldssr,      # value of `ssr' in the last iteration
          dh,          # nat. hom. modulo derived subgroup
          df,          # range of `dh'
          fs,          # list of factors of the size of `df'
          gen,         # generators for the next candidate
          pp,          # `p'-part of the size of `df'
          pu,          # Sylow `p' subgroup of `df'
          tmp,         # agemo generators
          ph,          # nat. hom. onto Frattini quotient of `pu'
          ff,          # Frattini factor
          ffsize,      # size of `ff'
          pcgs,        # PCGS of `ff'
          dim,         # dimension of the vector space `ff'
          field,       # prime field in char. `p'
          one,         # identity in `field'
          idm,         # identity matrix
          mg,          # matrices of `G' action on `ff'
          vsl,         # list of simult. eigenspaces
          nextvsl,     # for next iteration
          matrix,      # loop variable
          mat,         #
          eigenvalue,  # loop variable
          nullspace,   # generators of the eigenspace
          space,       # loop variable
          inter,       # intersection
          tmp2,        #
          v;           #

    ds  := [ G ];
    ssr := DerivedSubgroup( G );
    if Size( ssr ) < Size( G ) then
      ds[2]:= ssr;
    fi;

    if not IsTrivial( ssr ) then

      # Find a small generating system `gs' of `G'.
      # (We do *NOT* want to call `SmallGeneratingSet' here since
      # `MinimalGeneratingSet' is installed as a method for pc groups,
      # and for groups such as the Sylow 3 normalizer in F3+,
      # this needs more time than `SupersolvableResiduumDefault'.
      # Also the other method for `SmallGeneratingSet', which takes those
      # generators that cannot be omitted, is too slow.
      # The ``greedy'' type code below need not process all generators,
      # and it will be not too bad for pc groups.)
      gens := GeneratorsOfGroup( G );
      gs   := [ gens[1] ];
      p    := 2;
      o    := Order( gens[1] );
      size := Size( G );
      repeat
        s:= SubgroupNC( G, Concatenation( gs, [ gens[p] ] ) );
        if o < Size( s ) then
          Add( gs, gens[p] );
          o:= Size( s );
        fi;
        p:= p+1;
      until o = size;

      # Loop until we reach the residuum.
      repeat

        # Remember the last candidate as `oldssr'.
        oldssr := ssr;
        ssr    := DerivedSubgroup( oldssr );

        if Size( ssr ) < Size( oldssr ) then

          dh:= NaturalHomomorphismByNormalSubgroupNC( oldssr, ssr );

          # `df' is the commutator factor group `oldssr / ssr'.
          df:= Range( dh );
          SetIsAbelian( df, true );
          fs:= Factors(Integers, Size( df ) );

          # `gen' collects the generators for the next candidate
          gen := ShallowCopy( GeneratorsOfGroup( df ) );

          for p in Set( fs ) do

            pp:= Product( Filtered( fs, x -> x  = p ) );

            # `pu' is the Sylow `p' subgroup of `df'.
            pu:= SylowSubgroup( df, p );

            # Remove the `p'-part from the generators list `gen'.
            gen:= List( gen, x -> x^pp );

            # Add the agemo_1 of the Sylow subgroup to the generators list.
            tmp:= List( GeneratorsOfGroup( pu ), x -> x^p );
            Append( gen, tmp );
            ph:= NaturalHomomorphismByNormalSubgroupNC( pu,
                                                  SubgroupNC( df, tmp ) );

            # `ff' is the `p'-part of the Frattini factor group of `pu'.
            ff := Range( ph );
            ffsize:= Size( ff );
            if p < ffsize then

              # noncyclic case
              pcgs := Pcgs( ff );
              dim  := Length( pcgs );
              field:= GF(p);
              one  := One( field );
              idm  := IdentityMat( dim, field );

              # `mg' is the list of matrices of the action of `G' on the
              # dual space of the module, w.r.t. `pcgs'.
              mg:= List( gs, x -> TransposedMat( List( pcgs,
                     y -> one * ExponentsOfPcElement( pcgs, Image( ph,
                          Image( dh, PreImagesRepresentative(
                           dh, PreImagesRepresentative(ph,y) )^x ) ) )))^-1);
#T inverting is not necessary, or?
              mg:= Filtered( mg, x -> x <> idm );

              # `vsl' is a list of generators of all the simultaneous
              # eigenspaces.
              vsl:= [ idm ];
              for matrix in mg do

                nextvsl:= [];

                # All eigenvalues of `matrix' will be used.
                # (We expect `p' to be small, so looping over the nonzero
                # elements of the field is much faster than constructing and
                # factoring the characteristic polynomial of `matrix').
                mat:= matrix;
                for eigenvalue in [ 2 .. p ] do
                  mat:= mat - idm;
                  nullspace:= NullspaceMat( mat );
                  if not IsEmpty( nullspace ) then
                    for space in vsl do
                      inter:= SumIntersectionMat( space, nullspace )[2];
                      if not IsEmpty( inter ) then
                        Add( nextvsl, inter );
                      fi;
                    od;
                  fi;

                od;

                vsl:= nextvsl;

              od;

              # Now calculate the dual spaces of the eigenspaces.
              if IsEmpty( vsl ) then
                Append( gen, GeneratorsOfGroup( pu ) );
              else

                # `tmp' collects the eigenspaces.
                tmp:= [];
                for matrix in vsl do

                  # `tmp2' will be the base of the dual space.
                  tmp2:= [];
                  Append( tmp, matrix );
                  for v in NullspaceMat( TransposedMat( tmp ) ) do

                    # Construct a group element corresponding to
--> --------------------

--> maximum size reached

--> --------------------

[ Verzeichnis aufwärts0.55unsichere Verbindung  Übersetzung europäischer Sprachen durch Browser  ]