#############################################################################
##
#W  clasperm.gi                 GAP library                    Heiko Thei"sen
##
#H  @(#)$Id: clasperm.gi,v 4.36 2003/05/23 16:17:59 gap Exp $
##
#Y  Copyright (C)  1997,  Lehrstuhl D fuer Mathematik,  RWTH Aachen, Germany
#Y  (C) 1998 School Math and Comp. Sci., University of St.  Andrews, Scotland
#Y  Copyright (C) 2002 The GAP Group
##
##  This   file contains the functions   that calculate ordinary and rational
##  classes for permutation groups.
##
Revision.clasperm_gi :=
    "@(#)$Id: clasperm.gi,v 4.36 2003/05/23 16:17:59 gap Exp $";


#############################################################################
##
#M  Enumerator( <xorb> )  . . . . . . . . . for conj. classes in perm. groups
##
##  The only difference to the enumerator for external orbits is a better
##  `Position' (and `PositionCanonical') method.
##
BindGlobal( "NumberElement_ConjugacyClassPermGroup", function( enum, elm )
    local xorb, G, rep;

    xorb := UnderlyingCollection( enum );
    G := ActingDomain( xorb );
    rep := RepOpElmTuplesPermGroup( true, G, [ elm ],
                   [ Representative( xorb ) ],
                   TrivialSubgroup( G ), StabilizerOfExternalSet( xorb ) );
    if rep = fail  then
        return fail;
    else
        return PositionCanonical( enum!.rightTransversal, rep ^ -1 );
    fi;
end );

InstallMethod( Enumerator,
    [ IsConjugacyClassPermGroupRep ],
    xorb -> EnumeratorByFunctions( xorb, rec(
               NumberElement     := NumberElement_ConjugacyClassPermGroup,
               ElementNumber     := ElementNumber_ExternalOrbitByStabilizer,

               rightTransversal  := RightTransversal( ActingDomain( xorb ),
                   StabilizerOfExternalSet( xorb ) ) ) ) );


#############################################################################
##
#M  <cl1> = <cl2> . . . . . . . . . . . . . . . . . . . for conjugacy classes
##
InstallMethod( \=,"classes for perm group", IsIdenticalObj,
    [ IsConjugacyClassPermGroupRep, IsConjugacyClassPermGroupRep ],
    function( cl1, cl2 )
    if not IsIdenticalObj( ActingDomain( cl1 ), ActingDomain( cl2 ) )  then
        TryNextMethod();
    fi;
    return RepOpElmTuplesPermGroup( true, ActingDomain( cl1 ),
                   [ Representative( cl1 ) ],
                   [ Representative( cl2 ) ],
                   StabilizerOfExternalSet( cl1 ),
                   StabilizerOfExternalSet( cl2 ) ) <> fail;
end );

#############################################################################
##
#M  <g> in <cl> . . . . . . . . . . . . . . . . . . . . for conjugacy classes
##
InstallMethod( \in,"perm class rep", IsElmsColls,
  [ IsPerm, IsConjugacyClassPermGroupRep ],
function( g, cl )
local   G;

    if HasAsList(cl) or HasAsSSortedList(cl) then
      TryNextMethod();
    fi;
    G := ActingDomain( cl );
    return RepOpElmTuplesPermGroup( true, ActingDomain( cl ),
                   [ g ], [ Representative( cl ) ],
                   TrivialSubgroup( G ),
                   StabilizerOfExternalSet( cl ) ) <> fail;
end );


#############################################################################
##
#M  Enumerator( <rcl> ) . . . . . . . . .  of rational class in a perm. group
##
##  The only difference to the enumerator for rational classes is a better
##  `Position' (and `PositionCanonical') method.
##
BindGlobal( "NumberElement_RationalClassPermGroup", function( enum, elm )
    local   rcl,  G,  rep,  gal,  T,  pow,  t;

    rcl := UnderlyingCollection( enum );
    G   := ActingDomain( rcl );
    rep := Representative( rcl );
    gal := RightTransversalInParent( GaloisGroup( rcl ) );
    T := enum!.rightTransversal;
    for pow  in [ 1 .. Length( gal ) ]  do
	# if gal[pow]=0 then the rep is the identity , no need to worry.
        t := RepOpElmTuplesPermGroup( true, G,
                     [ elm ], [ rep ^ Int( gal[ pow ] ) ],
                     TrivialSubgroup( G ),
                     StabilizerOfExternalSet( rcl ) );
        if t <> fail  then
            break;
        fi;
    od;
    if t = fail  then
        return fail;
    else
        return ( pow - 1 ) * Length( T ) + PositionCanonical( T, t ^ -1 );
    fi;
end );

InstallMethod( Enumerator,
    [ IsRationalClassPermGroupRep ],
    rcl -> EnumeratorByFunctions( rcl, rec(
               NumberElement     := NumberElement_RationalClassPermGroup,
               ElementNumber     := ElementNumber_RationalClassGroup,

               rightTransversal  := RightTransversal( ActingDomain( rcl ),
                   StabilizerOfExternalSet( rcl ) ) ) ) );


InstallOtherMethod( CentralizerOp, [ IsRationalClassGroupRep ],
    StabilizerOfExternalSet );

#############################################################################
##
#M  <cl1> = <cl2> . . . . . . . . . . . . . . . . . . .  for rational classes
##
InstallMethod( \=, IsIdenticalObj, [ IsRationalClassPermGroupRep,
        IsRationalClassPermGroupRep ],
    function( cl1, cl2 )
    if ActingDomain( cl1 ) <> ActingDomain( cl2 )  then
        TryNextMethod();
    fi;
    # the Galois group of the identity is <0>, therefore we have to do this
    # extra test.
    return Order(Representative(cl1))=Order(Representative(cl2)) and
      ForAny( RightTransversalInParent( GaloisGroup( cl1 ) ), e ->
                   RepOpElmTuplesPermGroup( true, ActingDomain( cl1 ),
                           [ Representative( cl1 ) ],
                           [ Representative( cl2 ) ^ Int( e ) ],
                           StabilizerOfExternalSet( cl1 ),
                           StabilizerOfExternalSet( cl2 ) ) <> fail );
end );

#############################################################################
##
#M  <g> in <cl> . . . . . . . . . . . . . . . . . . . .  for rational classes
##
InstallMethod( \in, true, [ IsPerm, IsRationalClassPermGroupRep ], 0,
    function( g, cl )
    local   G;

    G := ActingDomain( cl );
    # the Galois group of the identity is <0>, therefore we have to do this
    # extra test.
    return Order(Representative(cl))=Order(g) and
     ForAny( RightTransversalInParent( GaloisGroup( cl ) ), e ->
                   RepOpElmTuplesPermGroup( true, G,
                           [ g ^ Int( e ) ],
                           [ Representative( cl ) ],
                           TrivialSubgroup( G ),
                           StabilizerOfExternalSet( cl ) ) <> fail );
end );

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

#F  CompleteGaloisGroupPElement( <cl>, <gal>, <power>, <p> )  add the p'-part
##
##  This  function assumes  that  the <p>-part  of the Galois  group  of  the
##  rational class <cl>  is already  bound  to  '<cl>.galoisGroup'.  It  then
##  computes  the  <p>'-part  and finds an  element  of the  normalizer which
##  induces an inner automorphism representing the generating residue of  the
##  Galois group.  <power> must the <p>-th power of <cl> .  If <p> = 2, there
##  is nothing to be done, since the Galois group is a 2-group then.
##
InstallGlobalFunction( CompleteGaloisGroupPElement, function( class, gal, power, p )
    local  G,  rep,  order,  F,
           phi,             # size of the prime residue class group
           primitiveRoot,   # generator of the cyclic prime residue class group
           sizeKnownPart,   # size of the known part of the Galois group
           sizeUnknownPart, # size of the unknown part of the Galois group
           generatorUnknownPart,
                            # generator of the unknown part of the prime
                            # residue class group, whose powers are tested
                            # one by one
           exp,             # some power of 'generatorP_Part'
           div,             # divisors of $p-1$
           q,               # variable used in division test
           fusingElement,   # element that  does the generating automorphism
           i;               # loop variable

    # If $p=2$, there is nothing to do.
    if p > 2  then
        G := ActingDomain( class );
        rep := Representative( class );
        order := Order( rep );
        F := FamilyObj( One( ZmodnZ( order ) ) );

        # <power> = 1 means that the power is the identity class.
        if power = 1  then
            power := RationalClass( G, One( G ) );
            SetStabilizerOfExternalSet( power, G );
            SetGaloisGroup( power, GroupByPrimeResidues( [  ], 1 ) );
            power!.fusingElement := One( G );
        fi;

        # Get the size of the prime residue class group and of the known part
        # of  the Galois  group (already known   from the calculation in  the
        # Sylow subgroup).
        phi                  := order / p * ( p - 1 );
        sizeKnownPart        := Size( gal );
        sizeUnknownPart      := GcdInt( p - 1, phi / sizeKnownPart );
        primitiveRoot        := ZmodnZObj( F, PrimitiveRootMod( order ) );
        generatorUnknownPart := primitiveRoot ^ ( phi / sizeUnknownPart );
        q := Size( G ) / Size( StabilizerOfExternalSet( class ) ) /
             sizeKnownPart;

        # Now run through all  the divisors <d> of  'sizeUnknownPart' testing
        # if there is an automorphism of order 'sizeKnownPart * <d>'.
        div           := DivisorsInt( sizeUnknownPart );
        i             := Length( div ) + 1;
        fusingElement := fail;
        repeat
            i := i - 1;

            # If such an automorphism exists, its order times the centralizer
            # order must divide the group order.
            if q mod div[ i ] = 0  then
                exp := generatorUnknownPart ^ ( sizeUnknownPart/div[i] );

                # If $C_G(g) = C_G(g^p)$, then Gal(<g>) must be generated
                # by a power of the generator of Gal(<g>^<p>).
                if Size( StabilizerOfExternalSet( class ) ) =
                   Size( StabilizerOfExternalSet( power ) )  then
                    if sizeKnownPart*div[i]>Size(GaloisGroup(power))  then
                        fusingElement := fail;
                    else
                        fusingElement := power!.fusingElement ^
                                         (Size(GaloisGroup(power)) /
                                          (sizeKnownPart*div[i]));
                        if rep ^ fusingElement <> rep ^ Int( exp )  then
                            fusingElement := fail;
                        fi;
                    fi;

                elif    order = p
                     or LogMod( Int( exp ), PrimitiveRootMod( order / p ),
                                order / p ) mod
                        IndexInParent( GaloisGroup( power ) ) = 0  then
                    if IsPerm( rep )  then
                        fusingElement := RepOpElmTuplesPermGroup( true, G,
                            [ rep ], [ rep ^ Int( exp ) ],
                            StabilizerOfExternalSet( class ),
                            StabilizerOfExternalSet( class ) );
                    else
                        fusingElement := RepresentativeAction( G,
                                         rep, rep ^ Int( exp ) );
                    fi;
                fi;
            fi;

        until fusingElement <> fail;

        # Construct the Galois  group as  subgroup of  a prime residue  class
        # group   and enter  the  conjugating   element   which induces   the
        # generating automorphism into the class record.
        gal := GroupByPrimeResidues(
                   [ primitiveRoot ^ ( phi / sizeKnownPart / div[ i ] ) ],
                   order );
        class!.fusingElement := fusingElement;

    fi;
    return gal;
end );

#############################################################################
##
#F  RatClasPElmArrangeClasses( <T>, <list>, <roots>, <power> )
##
InstallGlobalFunction( RatClasPElmArrangeClasses, function( T, list, roots, power )
    local  i,  j,  allRoots;

    allRoots := [ power ];
    for i  in [ 2 .. Length( T ) ]  do
        if T[ i ].power = power  then
            j := Length( list ) + 1;
            list[ j ] := i;
            roots[ j ] := [  ];
            Append( roots[ j ],RatClasPElmArrangeClasses(T,list,roots,i));
            Append( allRoots, roots[ j ] );
        fi;
    od;
    return allRoots;
end );

#############################################################################
##
#F  SortRationalClasses( <rationalClasses>, <p> ) . .  sort a list of classes
##
##  Sort the classes according to increasing  order, then decreasing <p>-part
##  of centralizer order, then decreasing <p>-part of Galois group order.
##
InstallGlobalFunction( SortRationalClasses, function( rationalClasses, p )
    Sort( rationalClasses, function( cl1, cl2 )
        local  ppart;
        if   Order( cl1.representative ) <
             Order( cl2.representative )  then
            return true;
        elif Order( cl1.representative ) >
             Order( cl2.representative )  then
            return false;
        else
            ppart := p ^ LogInt( Size( cl1.centralizer ), p );
            if Size( cl2.centralizer ) mod ppart <> 0  then
                return true;
            elif Size( cl2.centralizer ) mod ( ppart * p ) = 0  then
                return false;
            else
                ppart := p ^ LogInt( Size( cl1!.galoisGroup ), p );
                return Size( cl2!.galoisGroup ) mod ppart <> 0;
            fi;
        fi;
      end );
end );

#############################################################################
##
#F  FusionRationalClassesPSubgroup( <N>, <S>, <rationalClasses> )  pre-fusion
##
InstallGlobalFunction( FusionRationalClassesPSubgroup, function( N, S, rationalClasses )
    local  representatives,  classreps,  classimages,  fusedClasses,
           gens,  gensS,  gensNmodS,  genimages,  gen,
           prm,  i,  orbs,  orb,  cl,  pos,  porb;

    if Size( N ) > Size( S )  then

        # Construct the fusing operation of the group <N>.
        representatives := List( rationalClasses, cl -> cl.representative );
        classreps := [  ];
#        gens := TryPcgsPermGroup( [ N, S, TrivialSubgroup( N ) ],
#                        false, false, false );
#        if not IsPcgs( gens )  then
            gens := GeneratorsOfGroup( N );
#        fi;
        gensS := [  ];  gensNmodS := [  ];
        for gen  in gens  do
            if gen in S  then
                Add( gensS, gen );
            else
                Add( gensNmodS, gen );
                Append( classreps, OnTuples( representatives, gen ) );
            fi;
        od;
        classimages := List( RationalClassesSolvableGroup( S, 1,
	  rec(candidates:= classreps) ),
	  cl -> cl.representative );
        genimages := [  ];
        for i  in [ 1 .. Length( gensNmodS ) ]  do
            prm := List( [ 1 + ( i - 1 ) * Length( rationalClasses )
                           ..        i   * Length( rationalClasses ) ],
                   x -> Position( representatives, classimages[ x ] ) );
            Add( genimages, PermList( prm ) );
        od;
        orbs := ExternalOrbitsStabilizers( N,
                        [ 1 .. Length( rationalClasses ) ],
                        Concatenation( gensNmodS, gensS ),
                        Concatenation( genimages, List( gensS, g -> () ) ) );
                        # `genimages' arose from `PermList'
        fusedClasses := [  ];
        for orb  in orbs  do
            cl := rationalClasses[ Representative( orb ) ];
#
#T We may *NOT* set a known (larger) centralizer here as the centralizers
# themselves are used later to arrange the classes correctly (Lemma 3.3 in
# Heiko's diploma thesis, page 59/60).               AH
#
#            cl.centralizer := Centralizer
#                    ( StabilizerOfExternalSet( orb ), cl.representative,
#                      cl.centralizer );
            Add( fusedClasses, cl );
        od;

        # Update the `.power' entries.
        porb := [  ];
        for i  in [ 1 .. Length( fusedClasses ) ]  do
            pos := Position( representatives,
                             fusedClasses[ i ].power.representative );
            porb[ i ] := PositionProperty( orbs, o -> pos in AsList( o ) );
        od;
        for i  in [ 1 .. Length( fusedClasses ) ]  do
            fusedClasses[ i ].power := fusedClasses[ porb[ i ] ];
        od;

        return fusedClasses;
    else
        return rationalClasses;
    fi;
end );

#############################################################################
##
#F  RationalClassesPElements( <G>, <p> )  . .  rational classes of p-elements
##
InstallGlobalFunction( RationalClassesPElements, function( arg )
    local  G,               # the group
           p,               # the prime
           minprime,        # is <p> the minimal prime dividing $|G|$?
           sumSizes,        # sum of all class lengths known so far, optional
           rationalClasses, # rational classes of <p>-elements, result
           S,               # Sylow <p> subgroup of <G>
           gen,             # generator of <S> in the cyclic case
           N,               # solvable subgroup of N_G(S)
           rationalSClasses,# rational <S>-classes under conjugation by <N>
           list,            # list of class indices for order of treatment
           roots,           # list of indices of roots of a class
           found,           # classes already found
           movedTo,         # list of new positions of fused classes
           power,  gal,     # power and Galois group of current class
           i, j, cl, Scl;   # loop variables

    # Get the arguments.
    G := arg[ 1 ];
    p := arg[ 2 ];
    minprime :=  p = 2  or  p = Set( FactorsInt( Size( G ) ) )[ 1 ];
    if Length( arg ) > 2  then  sumSizes := arg[ 3 ];
                          else  sumSizes := -1;        fi;

    Info( InfoClasses, 1, "Calculating Sylow ", p, "-subgroup of |G| = ",
        Size( G ) );
    S := SylowSubgroup( G, p );

    # Treat the cyclic case.
    if IsCyclic( S )  then

        # Find a generator that generates the whole cyclic group.
        if IsTrivial( S )  then
            gen := One( S );
        else
            gen := First( GeneratorsOfGroup( S ),
                          gen -> Order( gen ) = Size( S ) );
        fi;

        rationalClasses := [  ];
        j := LogInt( Size( S ), p );
        for i  in [ 1 .. j ]  do
            cl := RationalClass( G, gen ^ ( p ^ ( j - i ) ) );
            SetStabilizerOfExternalSet( cl, Centralizer( G,
                    Representative( cl ), S ) );
            gal := GroupByPrimeResidues( [  ], p ^ i );
            if i = 1  then  power := 1;
                      else  power := rationalClasses[ i - 1 ];  fi;
            SetGaloisGroup( cl, CompleteGaloisGroupPElement
                    ( cl, gal, power, p ) );
            Add( rationalClasses, cl );
        od;
        return rationalClasses;
    fi;

    N := Normalizer( G, S );

    # Special treatment for elementary abelian Sylow subgroups.
    if IsElementaryAbelian( S )  then
        rationalClasses := RationalClassesInEANS( N, S );
        rationalSClasses := [  ];
        for cl  in rationalClasses  do
            Scl := rec( representative := Representative( cl ),
                           centralizer := StabilizerOfExternalSet( cl ),
                           galoisGroup := GroupByPrimeResidues( [  ],
                                          Order( Representative( cl ) ) ),
                                 power := rec( representative := One( S ) ) );
            Add( rationalSClasses, Scl );
        od;

    else
        Info( InfoClasses, 1,
              "Calculating rational classes in Sylow subgroup" );
        rationalSClasses := RationalClassesSolvableGroup( S, 3 );

        # Fuse the classes with the Sylow normalizer.
        rationalSClasses := FusionRationalClassesPSubgroup
                            ( N, S, rationalSClasses );

    fi;

    # Sort the classes. Change the `.power'  entries so that they contain the
    # index of the power class.
    SortRationalClasses( rationalSClasses, p );
    for cl  in rationalSClasses  do
        cl.power := PositionProperty( rationalSClasses,
                            c -> c.representative = cl.power.representative );
    od;
    Info( InfoClasses, 1, Length( rationalSClasses ), " classes to fuse" );

    # Determine the order in which to process the <S>-classes.
    list  := [ 1 ];
    roots := [ [  ] ];
    RatClasPElmArrangeClasses( rationalSClasses, list, roots, 1 );
    found   := [ 1 ];
    movedTo := [ 0 ];

    # Make <G>-classes out of the <N>-classes, putting them in a new list.
    rationalClasses := [  ];
    j := 1;
    while     j < Length( list )
          and sumSizes < Size( G )  do
        j := j + 1;
        if not list[ j ] in found  then
            Scl := rationalSClasses[ list[ j ] ];

            # If the class is  central, since we  have already considered the
            # Sylow  normalizer, it will not fuse to any other central class,
            # so it can be added to the list.
            if IsBound( Scl.isCentral )  then
                i := fail;
            else
                i := PositionProperty( rationalClasses, c -> ForAny
                  ( RightTransversalInParent( Scl.galoisGroup ), e ->
                     RepOpElmTuplesPermGroup( true, G,
                             [ Scl.representative ],
                             [ Representative( c ) ^ Int( e ) ],
                             Scl.centralizer,
                             StabilizerOfExternalSet( c ) ) <> fail ) );
            fi;
            if i = fail  then
                i := Length( rationalClasses ) + 1;
            fi;

            movedTo[ list[ j ] ] := i;
            if i > Length( rationalClasses )  then
                cl := RationalClass( G, Scl.representative );
                SetStabilizerOfExternalSet( cl, Centralizer( G,
                    Representative( cl ), Scl.centralizer ) );
                if movedTo[ Scl.power ] = 0  then
                    power := 1;
                else
                    power := rationalClasses[ movedTo[ Scl.power ] ];
                fi;
                if minprime  or  IsBound( Scl.isCentral )  then
                    SetGaloisGroup( cl, Scl.galoisGroup );
                else
                    SetGaloisGroup( cl, CompleteGaloisGroupPElement
                            ( cl, Scl.galoisGroup, power, p ) );
                fi;
                Add( rationalClasses, cl );
                if sumSizes >= 0  then
                    sumSizes := sumSizes + Size( cl );
                    Info( InfoClasses, 2, "Still missing ",
                          Size( G ) - sumSizes, " elements" );
                fi;
            else
                UniteSet( found, roots[ j ] );
            fi;
        fi;
    od;

    return rationalClasses;
end );

#############################################################################
##
#F  RationalClassesPermGroup(<G>[,<primes>]) rational classes for perm groups
##
InstallGlobalFunction( RationalClassesPermGroup, function( G, primes )
    local  rationalClasses,  # rational classes of <G>, result
           p,                # next (largest) prime to be processed
           pRationalClasses, # rational classes of <p>-elements in <G>
           pClass,           # one class from <pRationalClasses>
           z, r,             # <z> is the repr. of <pClass> of order <p>^<r>
           C,                # the centralizer of <z> in <G>
           Hom,              # block homomorphism determined by the cycles
                             # of <z>
           C_,               # image of <C> under <Hom>
           rationalClasses_, # rational classes in <C_>
           found,            # classes whose preimages are already found
           pos,              # position of class among constructed classes
           class_,           # one class from <rationalClasses_>
           y_, t,            # <y_> is the repr. of <class_> of order <t>
           moduli,           # moduli for Chinese remainder theorem
           y,  oy,           # preimage of <y_> that is a root of <z>, order
           s, rs, a, b, gcd, # auxiliary variables in the calculation of <y>
           class,            # class to be constructed from <y>
           ji,               # generator of the cyclic Galois group of <z>
           gi,               # element inducing the conjugation corr. to <ji>
           conj,             # result of conjugacy test $Hom(y^g)$ to $y_^m$
           m,                # auxiliary variable in calculation of $Gal(y)$
           gens,  gen,       # generators of the Galois group of <y>.
           i, k, cl;   # loop variables

    # Treat the trivial case.
    rationalClasses := [  ];
    if IsTrivial( G )  then
        return rationalClasses;
    fi;

    for k  in [ 1 .. Length( primes ) ]  do
        p := primes[ k ];
        if Size( G ) mod p = 0  then
            if     k = Length( primes )
               and IsSubset( primes, FactorsInt( Size( G ) ) )  then
                pRationalClasses := RationalClassesPElements( G, p,
                                        Sum( rationalClasses, Size ) );
            else
                pRationalClasses := RationalClassesPElements( G, p );
            fi;
            Append( rationalClasses, pRationalClasses );
            if k < Length( primes )  then
                if p = 2  then
                    Error( "case p = 2 not implemented" );
                fi;
                for pClass  in pRationalClasses  do
                    z := Representative( pClass );
                    C := StabilizerOfExternalSet( pClass );
                    r := LogInt( Order( z ), p );

                    # Set  up the  blocks homomorphism  C  -> C_ and find the
                    # rational classes in C_.
                    Hom := ActionHomomorphism( C, List( Cycles( z,
                                   MovedPoints( G ) ), Set ), OnSets );
                    C_ := ImagesSource( Hom );
                    rationalClasses_ := RationalClassesPermGroup
                        ( C_, primes{ [ k + 1 .. Length( primes ) ] } );

                    # Pull  back the rational classes and  the  Galois groups
                    # from C_ to C.
                    Info( InfoClasses, 1, "Lifting back from |C_| = ",
                        Size( C_ ), " to |G| = ", Size( G ) );
                    found := [  ];
                    for i  in [ 1 .. Length( rationalClasses_ ) ]  do
                      if not i in found  then
                        class_ := rationalClasses_[ i ];
                        y_ := Representative( class_ );
                        t := Order( y_ );
                        moduli := [ p ^ r, t ];

                        # Find  a preimage of <y_>  that really is  a root of
                        # <z>.
                        y   := PreImagesRepresentative( Hom, y_ );
                        s   := LogInt( Order( y ), p );
                        rs  := Maximum( r, s );
                        gcd := Gcdex( t, p ^ rs );
                        a   := gcd.coeff1;
                        b   := gcd.coeff2;
                        y   := y ^ ( b * p ^ rs ) * z ^ a;
                        oy  := Order( y );

                        # Let <g> be an element  conjugating <z> to $z^j$ and
                        # generating  $Gal(z)$. Find the smallest power $g^i$
                        # such that $Hom(y^{g^i})$ is rationally conjugate to
                        # $Hom(y)$. Then    $j^i$ times  a   cofactor is  the
                        # generator of  one  direct  factor of $Gal(y)$.  All
                        # preimages of elements $Hom(y^{g^l})$ with $l<i$ are
                        # rationally conjugate to <y>.
                        gi := One( G );
                        ji := One( GaloisGroup( pClass ) );
                        if not IsTrivial( GaloisGroup( pClass ) )  then
                          repeat
                            gi :=gi*pClass!.fusingElement;
                            ji :=ji*GeneratorsOfGroup(GaloisGroup(pClass))[1];
                            cl :=( y ^ gi ) ^ Hom;
                            pos := i - 1;
                            repeat
                              pos := pos + 1;
                              for m  in RightTransversalInParent
                                (GaloisGroup(rationalClasses_[pos]))  do
                                conj := RepOpElmTuplesPermGroup( true, C_,
                                  [ cl ], [ Representative
                                            (rationalClasses_[pos])^Int(m) ],
                                  TrivialSubgroup( C_ ),
                                  StabilizerOfExternalSet
                                                (rationalClasses_[ pos ]) );
                                if conj <> fail  then  break;  fi;
                              od;
                            until conj <> fail;
                            AddSet( found, pos );
                          until pos = i;
                        else
                          cl   := Representative( class_ );
                          m    := One( GaloisGroup( class_ ) );
                          conj := One( G );
                        fi;

                        # Now   $Hom(y^{g^i})  ~  Hom(y^m)$.  $Gal(y)$ is the
                        # direct product   of $Gal(Hom(y))$ and  the subgroup
                        # generated by $mj^i$.
                        gens := [ ChineseRem( moduli, [ Int(ji), Int(m) ] ) ];
                        for gen  in GeneratorsOfGroup( GaloisGroup( class_ ) )
                          do
                            Add( gens, ChineseRem( moduli, [ 1, Int(gen)] ) );
                        od;
                        class := RationalClass( G, y );
                        SetStabilizerOfExternalSet( class, Centralizer
                          ( PreImages( Hom, StabilizerOfExternalSet(class_) ),
                            y ) );
                        SetGaloisGroup( class, GroupByPrimeResidues
                                ( gens, oy ) );
                        Add( rationalClasses, class );
                      fi;
                    od;
                od;
            fi;
        fi;
    od;
    return rationalClasses;
end );

#############################################################################
##
#M  RationalClasses( <G> )  . . . . . . . . . . . . . . . . . . of perm group
##
InstallMethod( RationalClasses, "perm group", [ IsPermGroup ],
    function( G )
    local   cl;

    if IsPrimePowerInt( Size( G ) ) and not HasIsNilpotentGroup(G) then
        SetIsNilpotentGroup( G, true );
        return RationalClasses(G);
    else
        cl := RationalClass( G, One( G ) );
        SetStabilizerOfExternalSet( cl, G );
        SetGaloisGroup( cl, GroupByPrimeResidues( [  ], 1 ) );
        return Concatenation( [ cl ], RationalClassesPermGroup
                       ( G, Reversed( Set( FactorsInt( Size( G ) ) ) ) ) );
    fi;
end );

#############################################################################
##
#M  ConjugacyClasses( <G> )
##
InstallMethod( ConjugacyClasses, "perm group",
    [ IsPermGroup and HasRationalClasses ],
    G -> Concatenation( List( RationalClasses( G ),
		        DecomposedRationalClass ) ) );


#############################################################################
##
#E

