Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Impressum rcwagrp.gi   Sprache: unbekannt

 
Spracherkennung für: .gi vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

#############################################################################
##
#W  rcwagrp.gi                GAP4 Package `RCWA'                 Stefan Kohl
##
##  This file contains implementations of methods and functions for computing
##  with rcwa groups over
##
##    - the ring Z of the integers, over
##    - the ring Z^2, over
##    - the semilocalizations Z_(pi) of the ring of integers, and over
##    - the polynomial rings GF(q)[x] in one variable over a finite field.
##
##  See the definitions given in the file rcwamap.gd.
##
#############################################################################

#############################################################################
##
#S  Implications between the categories of rcwa groups. /////////////////////
##
#############################################################################

InstallTrueMethod( IsGroup,     IsRcwaGroup );
InstallTrueMethod( IsRcwaGroup, IsRcwaGroupOverZOrZ_pi );
InstallTrueMethod( IsRcwaGroupOverZOrZ_pi, IsRcwaGroupOverZ );
InstallTrueMethod( IsRcwaGroupOverZOrZ_pi, IsRcwaGroupOverZ_pi );
InstallTrueMethod( IsRcwaGroup, IsRcwaGroupOverZxZ );
InstallTrueMethod( IsRcwaGroup, IsRcwaGroupOverGFqx );

#############################################################################
##
#S  Implications of `IsRcwaGroup'. //////////////////////////////////////////
##
#############################################################################

InstallTrueMethod( CanComputeSizeAnySubgroup, IsRcwaGroup );
InstallTrueMethod( KnowsHowToDecompose,
                   IsRcwaGroup and HasGeneratorsOfGroup );

#############################################################################
##
#M  IsWholeFamily . . . . . . . . . . . . . for an rcwa group -- return false
##
InstallMethod( IsWholeFamily,
               "for an rcwa group -- return false (RCWA)", true,
               [ IsRcwaGroup ], 0, ReturnFalse );

#############################################################################
##
#S  Rcwa groups and lists of generators. ////////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  IsGeneratorsOfMagmaWithInverses( <l> ) . . .  for a list of rcwa mappings
##
##  Tests whether all rcwa mappings in the list <l> belong to the same
##  family and are bijective, hence generate a group.
##
InstallMethod( IsGeneratorsOfMagmaWithInverses,
               "for lists of rcwa mappings (RCWA)", true,
               [ IsListOrCollection ], 0,

  function ( l )
    if   ForAll(l,IsRcwaMapping)
    then return     Length(Set(List(l,FamilyObj))) = 1
                and ForAll(l,IsBijective); 
    else TryNextMethod(); fi;
  end );

#############################################################################
##
#M  AssignGeneratorVariables( <G> ) . .  for rcwa groups with at most 8 gen's
##
##  This method assigns the generators of <G> to global variables a, b, ... .
##  As all functions which assign anything one-letter global variables, it is
##  for interactive use only, and should not be called from inside code.
##
InstallMethod( AssignGeneratorVariables,
               "for rcwa groups with at most 8 generators (RCWA)",
               true, [ IsRcwaGroup ], 0,

  function ( G )

    local  gens, names, name, i;

    gens := GeneratorsOfGroup(G);
    if Length(gens) > 8 then TryNextMethod(); fi;
    names := "abcdefgh";
    for i in [1..Length(gens)] do
      name := names{[i]};
      if IsBoundGlobal(name) then
        if   IsReadOnlyGlobal(name)
        then Error("variable ",name," is read-only"); fi;
        UnbindGlobal(name);
        Info(InfoWarning,1,"The global variable ",name,
                           " has been overwritten.");
      fi;
      BindGlobal(name,gens[i]);
      MakeReadWriteGlobal(name);
    od;
    Print("The following global variables have been assigned: ");
    for i in [1..Length(gens)] do
      Print(names{[i]});
      if i < Length(gens) then Print(", "); fi;
    od;
    Print("\n");
  end );

#############################################################################
##
#S  Conversion of rcwa groups between standard and sparse representation. ///
##
#############################################################################

#############################################################################
##
#M  SparseRepresentation( <G> ) . . . . . . . . . . .  for rcwa groups over Z
##
InstallMethod( SparseRepresentation,
               "for rcwa groups over Z (RCWA)",
               true, [ IsRcwaGroupOverZ and HasGeneratorsOfGroup ], 0,

  function ( G )

    local  G_sparse;

    G_sparse := GroupByGenerators(List(GeneratorsOfGroup(G),SparseRep));
    if HasIsTame(G) then SetIsTame(G_sparse,IsTame(G)); fi;
    if   HasModulusOfRcwaMonoid(G)
    then SetModulusOfRcwaMonoid(G_sparse,ModulusOfRcwaMonoid(G)); fi;
    if HasSize(G) then SetSize(G_sparse,Size(G)); fi;
    if HasIsAbelian(G) then SetIsAbelian(G_sparse,IsAbelian(G)); fi;
    if HasIsPGroup(G) then
      SetIsPGroup(G_sparse,IsPGroup(G));
      if HasPrimePGroup(G) then SetPrimePGroup(G_sparse,PrimePGroup(G)); fi;
    fi;
    if   HasIsSolvableGroup(G)
    then SetIsSolvableGroup(G_sparse,IsSolvableGroup(G)); fi;
    if   HasIsPerfectGroup(G)
    then SetIsPerfectGroup(G_sparse,IsPerfectGroup(G)); fi;
    if   HasIsSimpleGroup(G)
    then SetIsSimpleGroup(G_sparse,IsSimpleGroup(G)); fi;
    if   IsNaturalCTP_Z(G)
    then SetFilterObj(G_sparse,IsNaturalCTP_Z); fi;
    return G_sparse;
  end );

#############################################################################
##
#M  StandardRepresentation( <G> ) . . . . . . . . . .  for rcwa groups over Z
##
InstallMethod( StandardRepresentation,
               "for rcwa groups over Z (RCWA)",
               true, [ IsRcwaGroupOverZ and HasGeneratorsOfGroup ], 0,

  function ( G )

    local  G_standard;

    G_standard := GroupByGenerators(List(GeneratorsOfGroup(G),StandardRep));
    if HasIsTame(G) then SetIsTame(G_standard,IsTame(G)); fi;
    if   HasModulusOfRcwaMonoid(G)
    then SetModulusOfRcwaMonoid(G_standard,ModulusOfRcwaMonoid(G)); fi;
    if HasSize(G) then SetSize(G_standard,Size(G)); fi;
    if HasIsAbelian(G) then SetIsAbelian(G_standard,IsAbelian(G)); fi;
    if HasIsPGroup(G) then
      SetIsPGroup(G_standard,IsPGroup(G));
      if HasPrimePGroup(G) then SetPrimePGroup(G_standard,PrimePGroup(G)); fi;
    fi;
    if   HasIsSolvableGroup(G)
    then SetIsSolvableGroup(G_standard,IsSolvableGroup(G)); fi;
    if   HasIsPerfectGroup(G)
    then SetIsPerfectGroup(G_standard,IsPerfectGroup(G)); fi;
    if   HasIsSimpleGroup(G)
    then SetIsSimpleGroup(G_standard,IsSimpleGroup(G)); fi;
    if   IsNaturalCTP_Z(G)
    then SetFilterObj(G_standard,IsNaturalCTP_Z); fi;
    return G_standard;
  end );

#############################################################################
##
#S  Methods for `View', `Print', `Display' etc. /////////////////////////////
##
#############################################################################

#############################################################################
##
#M  ViewObj( <G> ) . . . . . . . . . . . . . . . . . . . . .  for rcwa groups
##
InstallMethod( ViewObj,
               "for rcwa groups (RCWA)", true,
               [ IsRcwaGroup and HasGeneratorsOfGroup ], 0,

  function ( G )

    local  NrGens, RingString, gens, g, cl, i;

    RingString := RingToString(Source(One(G)));
    if IsTrivial(G) then
      Print("Trivial rcwa group over ",RingString);
    elif     IsRcwaGroupOverZ(G) and Length(GeneratorsOfGroup(G)) <=6
         and ForAll(GeneratorsOfGroup(G),IsClassTransposition)
    then
      gens := GeneratorsOfGroup(G);
      Print("<");
      for i in [1..Length(gens)] do
        g  := gens[i];
        cl := TransposedClasses(g);
        Print("(",Residue(cl[1]),"(",Mod(cl[1]),"),",
                  Residue(cl[2]),"(",Mod(cl[2]),"))");
        if i < Length(gens) then Print(","); fi;
      od;
      Print(">");
    else
      Print("<");
      if   HasIsTame(G) and not (HasSize(G) and IsInt(Size(G)))
      then if IsTame(G) then Print("tame "); else Print("wild "); fi; fi;
      NrGens := Length(GeneratorsOfGroup(G));
      Print("rcwa group over ",RingString," with ",NrGens," generator");
      if NrGens > 1 then Print("s"); fi;
      if not (HasIsTame(G) and not IsTame(G)) then
        if   HasIdGroup(G)
        then Print(", of isomorphism type ",IdGroup(G));
        elif HasSize(G)
        then Print(", of order ",Size(G)); fi;
      fi;
      Print(">");
    fi;
  end );

#############################################################################
##
#M  ViewObj( <G> ) . . . . . . . . . for rcwa groups without known generators
##
InstallMethod( ViewObj,
               "for rcwa groups without known generators (RCWA)",
               true, [ IsRcwaGroup ], 0,

  function ( G )
    Print("<");
    if   HasIsTame(G)
    then if IsTame(G) then Print("tame "); else Print("wild "); fi; fi;
    Print("rcwa group over ",RingToString(Source(One(G))));
    if   HasElementTestFunction(G)
    then Print(", with membership test"); fi;
    if   not HasGeneratorsOfGroup(G)
    then Print(", without known generators"); fi;
    Print(">");
  end );

#############################################################################
##
#M  ViewString( <G> ) . . . for groups generated by class transpositions of Z
##
InstallMethod( ViewString,
               "for a group generated by class transpositions of Z",
               true, [ IsRcwaGroupOverZ and HasGeneratorsOfGroup ], 0,

  function ( G )

    local  gens, s, g, gs, cl;

    gens := GeneratorsOfGroup(G);
    if not ForAll(gens,IsClassTransposition) then TryNextMethod(); fi;
    s := "<";
    for g in gens do
      cl := TransposedClasses(g);
      gs := Concatenation(List(["(",Residue(cl[1]),"(",Mod(cl[1]),"),",
                                    Residue(cl[2]),"(",Mod(cl[2]),"))"],
                               String));
      if s <> "<" then s := Concatenation(s,","); fi;
      s := Concatenation(s,gs);
    od;
    s := Concatenation(s,">");
    return s;
  end );

#############################################################################
##
#M  ViewObj( <RG> ) . . . . . . . . . . . . .  for group rings of rcwa groups
##
InstallMethod( ViewObj,
               "for group rings of rcwa groups (RCWA)",
               ReturnTrue, [ IsGroupRing ], 100,

  function ( RG )

    local  R, G;

    R := LeftActingDomain(RG); G := UnderlyingMagma(RG);
    if not IsRcwaGroup(G) or R <> Source(One(G)) then TryNextMethod(); fi;
    Print(RingToString(R)," "); ViewObj(G);
  end );

#############################################################################
##
#M  Print( <G> ) . . . . . . . . . . . . . . . . . . . . . .  for rcwa groups
##
InstallMethod( PrintObj,
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )
    Print( "Group( ", GeneratorsOfGroup( G ), " )" );
  end );

#############################################################################
##
#M  Display( <G> ) . . . . . . . . . . . . . . . . . . . . .  for rcwa groups
##
InstallMethod( Display,
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )

    local  RingString, g, prefix;

    RingString := RingToString(Source(One(G)));
    if   IsTrivial(G) 
    then Print("Trivial rcwa group over ",RingString);
         if ValueOption("NoLineFeed") <> true then Print("\n"); fi;
    else prefix := false;
         if HasIsTame(G) and not (HasSize(G) and IsInt(Size(G))) then
           if IsTame(G) then Print("\nTame "); else Print("\nWild "); fi;
           prefix := true;
         fi;
         if prefix then Print("rcwa "); else Print("\nRcwa "); fi;
         Print("group over ",RingString);
         if not (HasIsTame(G) and not IsTame(G)) then
           if   HasIdGroup(G) then Print(" of isomorphism type ",IdGroup(G));
           elif HasSize(G)    then Print(" of order ",Size(G)); fi;
         fi;
         Print(", generated by\n\n[\n");
         for g in GeneratorsOfGroup(G) do Display(g); od;
         Print("]\n\n");
    fi;
  end );

#############################################################################
##
#M  LaTeXStringRcwaGroup( <G> ) . . . . . . . . . . . . . . . for rcwa groups
##
InstallMethod( LaTeXStringRcwaGroup,
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )

    local  s, g;

    s := "\\langle ";
    for g in GeneratorsOfGroup(G) do
      if s <> "\\langle " then Append(s,", "); fi;
      Append(s,LaTeXStringRcwaMapping(g));
    od;
    Append(s," \\rangle");
    return s;
  end );

#############################################################################
##
#S  The trivial rcwa groups. ////////////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#V  TrivialRcwaGroupOverZ . . . . . . . . . . . . . trivial rcwa group over Z
#V  TrivialRcwaGroupOverZxZ . . . . . . . . . . . trivial rcwa group over Z^2
##
BindGlobal( "TrivialRcwaGroupOverZ", Group( IdentityRcwaMappingOfZ ) );
BindGlobal( "TrivialRcwaGroupOverZxZ", Group( IdentityRcwaMappingOfZxZ ) );

#############################################################################
##
#M  TrivialSubmagmaWithOne( <G> ). . . . . . . . . . . . . .  for rcwa groups
##
InstallMethod( TrivialSubmagmaWithOne,
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )

    local  triv;

    triv := Group(One(G));
    SetParentAttr(triv,G);
    return triv;
  end );

#############################################################################
##
#S  The construction of the groups RCWA(R) of all rcwa permutations of R. ///
##
#############################################################################

#############################################################################
##
#M  RCWACons( IsRcwaGroup, Integers ) . . . . . . . . . . . . . . . RCWA( Z )
##
##  Returns the group which is formed by all bijective rcwa mappings of Z.
##
InstallMethod( RCWACons,
               "natural RCWA(Z) (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsIntegers ], 0,

  function ( filter, R )

    local  G, id;

    id := IdentityRcwaMappingOfZ;
    G  := Objectify( NewType( FamilyObj( Group( id ) ),
                              IsRcwaGroupOverZ and IsAttributeStoringRep ),
                     rec( ) );
    SetIsTrivial( G, false );
    SetOne( G, IdentityRcwaMappingOfZ );
    SetFilterObj( G, IsNaturalRCWA_OR_CT );
    SetFilterObj( G, IsNaturalRCWA );
    SetFilterObj( G, IsNaturalRCWA_Z );
    SetModulusOfRcwaMonoid( G, 0 );
    SetMultiplier( G, infinity );
    SetDivisor( G, infinity );
    SetIsFinite( G, false );
    SetSize( G, infinity );
    SetIsFinitelyGeneratedGroup( G, false );
    SetCentre( G, TrivialRcwaGroupOverZ );
    SetIsAbelian( G, false );
    SetIsSolvableGroup( G, false );
    SetIsPerfectGroup( G, false );
    SetIsSimpleGroup( G, false );
    SetRepresentative( G, RcwaMapping( [ [ -1, 0, 1 ] ] ) );
    SetName( G, "RCWA(Z)" );
    SetStructureDescription( G, Name( G ) );
    return G;
  end );

#############################################################################
##
#M  RCWACons( IsRcwaGroup, Integers^2 ) . . . . . . . . . . . . . RCWA( Z^2 )
##
##  Returns the group which is formed by all bijective rcwa mappings of Z^2.
##
InstallMethod( RCWACons,
               "natural RCWA(Z^2) (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsRowModule ], 0,

  function ( filter, R )

    local  G, id;

    if not IsZxZ( R ) then TryNextMethod( ); fi;

    id := IdentityRcwaMappingOfZxZ;
    G  := Objectify( NewType( FamilyObj( Group( id ) ),
                              IsRcwaGroupOverZxZ and IsAttributeStoringRep ),
                     rec( ) );
    SetIsTrivial( G, false );
    SetOne( G, id );
    SetFilterObj( G, IsNaturalRCWA_OR_CT );
    SetFilterObj( G, IsNaturalRCWA );
    SetFilterObj( G, IsNaturalRCWA_ZxZ );
    SetModulusOfRcwaMonoid( G, [ [ 0, 0 ], [ 0, 0 ] ] );
    SetMultiplier( G, infinity );
    SetDivisor( G, infinity );
    SetIsFinite( G, false );
    SetSize( G, infinity );
    SetIsFinitelyGeneratedGroup( G, false );
    SetCentre( G, TrivialRcwaGroupOverZxZ );
    SetIsAbelian( G, false );
    SetIsSolvableGroup( G, false );
    SetRepresentative( G, RcwaMapping( R, [ [ 1, 0 ], [ 0, 1 ] ],
      [ [ [ 0, 0 ], [ [ [ -1, 0 ], [ 0, -1 ] ], [ 0, 0 ], 1 ] ] ] ) );
    SetName( G, "RCWA(Z^2)" );
    SetStructureDescription( G, Name( G ) );
    return G;
  end );

#############################################################################
##
#M  RCWACons( IsRcwaGroup, Z_pi( <pi> ) ) . . . . . . . . . .  RCWA( Z_(pi) )
##
##  Returns the group which is formed by all bijective rcwa mappings
##  of Z_(pi).
##
InstallMethod( RCWACons,
               "natural RCWA(Z_(pi)) (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsZ_pi ], 0,

  function ( filter, R )

    local  G, pi, id;

    pi := NoninvertiblePrimes( R );
    id := RcwaMapping( pi, [ [1,0,1] ] ); IsOne( id );
    G  := Objectify( NewType( FamilyObj( Group( id ) ),
                                  IsRcwaGroupOverZ_pi
                              and IsAttributeStoringRep ),
                     rec( ) );
    SetIsTrivial( G, false );
    SetOne( G, id );
    SetFilterObj( G, IsNaturalRCWA_OR_CT );
    SetFilterObj( G, IsNaturalRCWA );
    SetFilterObj( G, IsNaturalRCWA_Z_pi );
    SetModulusOfRcwaMonoid( G, 0 );
    SetMultiplier( G, infinity );
    SetDivisor( G, infinity );
    SetIsFinite( G, false );
    SetSize( G, infinity );
    SetCentre( G, Group( id ) );
    SetIsAbelian( G, false );
    SetIsSolvableGroup( G, false );
    SetRepresentative( G, -id );
    SetName( G, Concatenation( "RCWA(", ViewString(R), ")" ) );
    SetStructureDescription( G, Name( G ) );
    return G;
  end );

#############################################################################
##
#M  RCWACons( IsRcwaGroup, PolynomialRing( GF( <q> ), 1 ) )  RCWA( GF(q)[x] )
##
##  Returns the group which is formed by all bijective rcwa mappings
##  of GF(q)[x].
##
InstallMethod( RCWACons,
               "natural RCWA(GF(q)[x]) (RCWA)", ReturnTrue, 
               [ IsRcwaGroup, IsUnivariatePolynomialRing ], 0,

  function ( filter, R )

    local  G, q, id, rep;

    q  := Size( CoefficientsRing( R ) );
    id := RcwaMapping( q, One(R), [ [1,0,1] * One(R) ] ); IsOne( id );
    G  := Objectify( NewType( FamilyObj( Group( id ) ),
                                  IsRcwaGroupOverGFqx
                              and IsAttributeStoringRep ),
                     rec( ) );
    SetIsTrivial( G, false );
    SetOne( G, id );
    SetFilterObj( G, IsNaturalRCWA_OR_CT );
    SetFilterObj( G, IsNaturalRCWA );
    SetFilterObj( G, IsNaturalRCWA_GFqx );
    SetModulusOfRcwaMonoid( G, Zero( R ) );
    SetMultiplier( G, infinity );
    SetDivisor( G, infinity );
    SetIsFinite( G, false );
    SetSize( G, infinity );
    SetIsFinitelyGeneratedGroup( G, false );
    SetCentre( G, Group( id ) );
    SetIsAbelian( G, false );
    SetIsSolvableGroup( G, false );
    rep := ClassTransposition(SplittedClass(R,q){[1..2]});
    SetRepresentative( G, rep );
    SetName( G, Concatenation( "RCWA(GF(", String(q), ")[",
                String(IndeterminatesOfPolynomialRing(R)[1]),"])" ) );
    SetStructureDescription( G, Name( G ) );
    return G;
  end );

#############################################################################
##
#F  RCWA( <R> ) . . . . . . . . . . . . . . . . . . . . . . . . . . RCWA( R )
##
InstallGlobalFunction( RCWA, R -> RCWACons( IsRcwaGroup, R ) );

#############################################################################
##
#S  The construction of the groups CT(R) ////////////////////////////////////
#S  generated by all class transpositions. //////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  CTCons( IsRcwaGroup, Integers ) . . . . . . . . . . . . . . . . . CT( Z )
##
##  Returns the simple group which is generated
##  by the set of all class transpositions of Z.
##
InstallMethod( CTCons,
               "natural CT(Z) (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsIntegers ], 0,

  function ( filter, R )

    local  G, id;

    id := IdentityRcwaMappingOfZ;
    G  := Objectify( NewType( FamilyObj( Group( id ) ),
                              IsRcwaGroupOverZ and IsAttributeStoringRep ),
                     rec( ) );
    SetIsTrivial( G, false );
    SetOne( G, SparseRep( IdentityRcwaMappingOfZ ) );
    SetFilterObj( G, IsNaturalRCWA_OR_CT );
    SetFilterObj( G, IsNaturalCT );
    SetFilterObj( G, IsNaturalCT_Z );
    SetModulusOfRcwaMonoid( G, 0 );
    SetMultiplier( G, infinity );
    SetDivisor( G, infinity );
    SetIsFinite( G, false );
    SetSize( G, infinity );
    SetIsFinitelyGeneratedGroup( G, false );
    SetCentre( G, SparseRep( TrivialRcwaGroupOverZ ) );
    SetIsAbelian( G, false );
    SetIsSolvableGroup( G, false );
    SetIsPerfectGroup( G, true );
    SetIsSimpleGroup( G, true );
    SetRepresentative( G, SparseRep( ClassTransposition(0,2,1,2) ) );
    SetSupport( G, Integers );
    SetName( G, "CT(Z)" );
    SetStructureDescription( G, Name( G ) );
    return G;
  end );

#############################################################################
##
#M  CTCons( IsRcwaGroup, <P>, Integers ) . . . . . . . . . . . . . CT( P, Z )
##
##  Returns the group which is generated by the set of all
##  class transpositions of Z which interchange residue classes whose
##  moduli have only prime factors in the finite set <P>.
##  In case 2 is an element of <P>, this group is simple.
##
InstallMethod( CTCons,
               "natural CT(Z) (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsList, IsIntegers ], 0,

  function ( filter, P, R )

    local  G, id;

    if   not ForAll(P,IsPosInt) or not ForAll(P,IsPrimeInt)
    then TryNextMethod(); fi;

    if P = [] then return TrivialRcwaGroupOverZ; fi;

    id := IdentityRcwaMappingOfZ;
    G  := Objectify( NewType( FamilyObj( Group( id ) ),
                              IsRcwaGroupOverZ and IsAttributeStoringRep ),
                     rec( ) );
    SetIsTrivial( G, false );
    SetOne( G, SparseRep( IdentityRcwaMappingOfZ ) );
    SetFilterObj( G, IsNaturalCTP_Z );
    SetModulusOfRcwaMonoid( G, 0 );
    SetMultiplier( G, infinity );
    SetDivisor( G, infinity );
    SetPrimeSet( G, P );
    SetIsFinite( G, false );
    SetSize( G, infinity );
    SetIsFinitelyGeneratedGroup( G, true );
    SetCentre( G, SparseRep( TrivialRcwaGroupOverZ ) );
    SetIsAbelian( G, false );
    SetIsSolvableGroup( G, false );
    if 2 in P then
      SetIsPerfectGroup( G, true );
      SetIsSimpleGroup( G, true );
    fi;
    SetRepresentative( G, SparseRep(ClassTransposition(0,P[1],1,P[1])) );
    SetSupport( G, Integers );
    SetName( G, Concatenation( "CT_", String(P), "(Z)" ) );
    SetStructureDescription( G, Name( G ) );
    if P = [2] then # Higman-Thompson group / Thompson's group V
      SetGeneratorsOfGroup(G,List([[0,2,1,2],[1,2,2,4],[0,2,1,4],[1,4,2,4]],
                                  c->SparseRep(ClassTransposition(c))));
    elif P = [3] then
      SetGeneratorsOfGroup(G,List([[0,3,1,3],[1,3,2,3],[2,9,3,9],[5,9,6,9],
                                   [2,3,3,9]],
                                  c->SparseRep(ClassTransposition(c))));
    elif P = [2,3] then
      SetGeneratorsOfGroup(G,List([[0,2,1,2],[0,3,1,3],[1,3,2,3],
                                   [0,2,1,4],[0,2,5,6],[0,3,1,6]],
                                  c->SparseRep(ClassTransposition(c))));
    fi;
    return G;
  end );

#############################################################################
##
#M  CTCons( IsRcwaGroup, Integers^2 ) . . . . . . . . . . . . . . . CT( Z^2 )
##
##  Returns the group which is generated by
##  the set of all class transpositions of Z^2.
##
InstallMethod( CTCons,
               "natural CT(Z^2) (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsRowModule ], 0,

  function ( filter, R )

    local  G, id;

    if not IsZxZ(R) then TryNextMethod(); fi;

    id := IdentityRcwaMappingOfZxZ;
    G  := Objectify( NewType( FamilyObj( Group( id ) ),
                              IsRcwaGroupOverZxZ and IsAttributeStoringRep ),
                     rec( ) );
    SetIsTrivial( G, false );
    SetOne( G, IdentityRcwaMappingOfZxZ );
    SetFilterObj( G, IsNaturalRCWA_OR_CT );
    SetFilterObj( G, IsNaturalCT );
    SetFilterObj( G, IsNaturalCT_ZxZ );
    SetModulusOfRcwaMonoid( G, [ [ 0, 0 ], [ 0, 0 ] ] );
    SetMultiplier( G, infinity );
    SetDivisor( G, infinity );
    SetIsFinite( G, false );
    SetSize( G, infinity );
    SetIsFinitelyGeneratedGroup( G, false );
    SetCentre( G, TrivialRcwaGroupOverZxZ );
    SetIsAbelian( G, false );
    SetIsSolvableGroup( G, false );
    SetIsPerfectGroup( G, true );
    SetIsSimpleGroup( G, true );
    SetRepresentative( G, ClassTransposition([0,0],[[1,0],[0,2]],
                                             [0,1],[[1,0],[0,2]]) );
    SetSupport( G, R );
    SetName( G, "CT(Z^2)" );
    SetStructureDescription( G, Name( G ) );
    return G;
  end );

#############################################################################
##
#M  CTCons( IsRcwaGroup, Z_pi( <pi> ) ) . . . . . . . . . . . .  CT( Z_(pi) )
##
##  Returns the group which is generated by
##  the set of all class transpositions of Z_(pi).
##
InstallMethod( CTCons,
               "natural CT(Z_(pi)) (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsZ_pi ], 0,

  function ( filter, R )

    local  G, pi, id, rep;

    pi := NoninvertiblePrimes( R );
    id := RcwaMapping( pi, [ [1,0,1] ] ); IsOne( id );
    G  := Objectify( NewType( FamilyObj( Group( id ) ),
                                  IsRcwaGroupOverZ_pi
                              and IsAttributeStoringRep ),
                     rec( ) );
    SetIsTrivial( G, false );
    SetOne( G, id );
    SetFilterObj( G, IsNaturalRCWA_OR_CT );
    SetFilterObj( G, IsNaturalCT );
    SetFilterObj( G, IsNaturalCT_Z_pi );
    SetModulusOfRcwaMonoid( G, 0 );
    SetMultiplier( G, infinity );
    SetDivisor( G, infinity );
    SetIsFinite( G, false );
    SetSize( G, infinity );
    SetCentre( G, Group( id ) );
    SetIsAbelian( G, false );
    SetIsSolvableGroup( G, false );
    rep := ClassTransposition(AllResidueClassesModulo(R,pi[1]){[1..2]});
    SetRepresentative( G, rep );
    SetSupport( G, R );
    SetName( G, Concatenation( "CT(", ViewString(R), ")" ) );
    SetStructureDescription( G, Name( G ) );
    return G;
  end );

#############################################################################
##
#M  CTCons( IsRcwaGroup, PolynomialRing( GF( <q> ), 1 ) ) . .  CT( GF(q)[x] )
##
##  Returns the group which is generated by
##  the set of all class transpositions of GF(q)[x].
##
InstallMethod( CTCons,
               "natural CT(GF(q)[x]) (RCWA)", true, 
               [ IsRcwaGroup, IsUnivariatePolynomialRing ], 0,

  function ( filter, R )

    local  G, q, id, rep;

    q  := Size( CoefficientsRing( R ) );
    id := RcwaMapping( q, One(R), [ [1,0,1] * One(R) ] ); IsOne( id );
    G  := Objectify( NewType( FamilyObj( Group( id ) ),
                                  IsRcwaGroupOverGFqx
                              and IsAttributeStoringRep ),
                     rec( ) );
    SetIsTrivial( G, false );
    SetOne( G, id );
    SetFilterObj( G, IsNaturalRCWA_OR_CT );
    SetFilterObj( G, IsNaturalCT );
    SetFilterObj( G, IsNaturalCT_GFqx );
    SetModulusOfRcwaMonoid( G, Zero( R ) );
    SetMultiplier( G, infinity );
    SetDivisor( G, infinity );
    SetIsFinite( G, false );
    SetSize( G, infinity );
    SetIsFinitelyGeneratedGroup( G, false );
    SetCentre( G, Group( id ) );
    SetIsAbelian( G, false );
    SetIsSolvableGroup( G, false );
    rep := ClassTransposition(SplittedClass(R,q){[1..2]});
    SetRepresentative( G, rep );
    SetSupport( G, R );
    SetName( G, Concatenation( "CT(GF(", String(q), ")[",
                String(IndeterminatesOfPolynomialRing(R)[1]),"])" ) );
    SetStructureDescription( G, Name( G ) );
    return G;
  end );

#############################################################################
##
#F  CT( <R> ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CT(R)
##
InstallGlobalFunction( CT, 

  function ( arg )

    local  P, R;

    if Length(arg) = 1 then
      R := arg[1];
      return CTCons( IsRcwaGroup, R );
    elif Length(arg) = 2 then
      P := arg[1];
      R := arg[2];
      return CTCons( IsRcwaGroup, P, R );
    else Error("usage: CT( [ <P>, ], <R> )"); fi;
  end );

#############################################################################
##
#M  Display( <G> ) . . . . . . . . . . . . . . . . . .  for RCWA(R) and CT(R)
##
InstallMethod( Display,
               "for RCWA(R) and CT(R) (RCWA)", true,
               [ IsNaturalRCWA_OR_CT ], 0,
               function ( G ) Print( Name( G ), "\n" ); end );

#############################################################################
##
#S  The membership- / subgroup test for RCWA(R) and CT(R). //////////////////
##
#############################################################################

#############################################################################
##
#M  \in( <g>, RCWA( <R> ) ) . . . . . . . . . for an rcwa mapping and RCWA(R)
##
InstallMethod( \in,
               "for an rcwa mapping and RCWA(R) (RCWA)", ReturnTrue,
               [ IsRcwaMapping, IsNaturalRCWA ], 100,

  function ( g, RCWA_R )
    return FamilyObj(g) = FamilyObj(One(RCWA_R)) and IsBijective(g);
  end );

#############################################################################
##
#M  \in( <g>, CT( <R> ) ) . . . . . . . . . . . for an rcwa mapping and CT(R)
##
InstallMethod( \in,
               "for an rcwa mapping and CT(R) (RCWA)", ReturnTrue,
               [ IsRcwaMapping, IsNaturalCT ], 100,

  function ( g, CT_R )

    local  R, numres;

    if   FamilyObj(g) <> FamilyObj(One(CT_R)) or not IsBijective(g)
    then return false; elif IsOne(g) then return true; fi;
    R := Support(CT_R);
    if IsIntegers(R) or IsZ_pi(R) then 
      if not IsClassWiseOrderPreserving(g) then return false; fi;
      if IsIntegers(R) and not IsSignPreserving(g) then return false; fi;
      if   Minimum([0..Mod(g)-1]^g) < 0
        or Maximum([-Mod(g)..-1]^g) >= 0
      then return false; fi;
    elif IsZxZ(R) then
      numres := AbsInt(DeterminantMat(Modulus(g)));
      if   not IsClassWiseOrderPreserving(g)
        or not ForAll(Coefficients(g),c->IsUpperTriangularMat(c[1]))
        or not ForAll(Coefficients(g),c->c[1][1][1] > 0)
        or not ForAll(Cartesian([0..numres-1],[-numres..numres]),
                      t->t^g*[1,0]>=0)
      then return false; fi;
    fi;
    if   ForAll(FactorizationIntoCSCRCT(g),
                gi ->    IsClassTransposition(gi)
                      or IsPrimeSwitch(gi) or IsPrimeSwitch(gi^-1))
    then return true; fi;
    TryNextMethod();
  end );

#############################################################################
##
#M  \in( <g>, CT( <P>, Integers ) ) . .  for an rcwa mapping of Z and CT_P(Z)
##
InstallMethod( \in,
               "for an rcwa mapping and CT_P(Z) (RCWA)", ReturnTrue,
               [ IsRcwaMappingOfZ, IsNaturalCTP_Z ], 100,

  function ( g, CTP_Z )

    local  R, numres;

    if not IsClassWiseOrderPreserving(g) or not IsSignPreserving(g)
      or not IsSubset(PrimeSet(CTP_Z),PrimeSet(g))
    then return false; fi;
    if   ForAll( FactorizationIntoCSCRCT(g),
                 gi -> (   IsClassTransposition(gi)
                        or IsPrimeSwitch(gi) or IsPrimeSwitch(gi^-1) )
                    and IsSubset( PrimeSet(CTP_Z), PrimeSet(gi) ) )
    then return true; fi;
    TryNextMethod();
  end );

#############################################################################
## 
#M  IsSubset( RCWA( <R> ), G ) . . . . . . . .  for RCWA(R) and an rcwa group
## 
InstallMethod( IsSubset,
               "for RCWA(R) and an rcwa group (RCWA)", ReturnTrue,
               [ IsNaturalRCWA, IsRcwaGroup ], SUM_FLAGS,

  function ( RCWA_R, G )
    return FamilyObj(One(RCWA_R)) = FamilyObj(One(G));
  end );

#############################################################################
## 
#M  IsSubset( CT( Integers ), G ) . . . .  for CT(Z) and an rcwa group over Z
## 
InstallMethod( IsSubset,
               "for CT(Z) and an rcwa group over Z (RCWA)", ReturnTrue,
               [ IsNaturalCT_Z, IsRcwaGroupOverZ and HasGeneratorsOfGroup ], SUM_FLAGS,

  function ( CT_Z, G )
    if not IsClassWiseOrderPreserving(G)
      or ForAny(GeneratorsOfGroup(G),g->Determinant(g)<>0)
      or Minimum(Ball(G,0,4,OnPoints)) < 0
    then return false; fi;
    return ForAll(GeneratorsOfGroup(G),g->g in CT_Z);
  end );

#############################################################################
## 
#M  IsSubset( CT( <R> ), G ) . . . . . . . . . .  for CT(R) and an rcwa group
## 
InstallMethod( IsSubset,
               "for CT(R) and an rcwa group (RCWA)", ReturnTrue,
               [ IsNaturalCT, IsRcwaGroup and HasGeneratorsOfGroup ], 100,

  function ( CT_R, G )
    if FamilyObj(One(G)) <> FamilyObj(One(CT_R)) then return false; fi;
    return ForAll(GeneratorsOfGroup(G),g->g in CT_R);
  end );

#############################################################################
## 
#M  IsSubset( CT( <R> ), RCWA( <R> ) ) . . . . . . . .  for CT(R) and RCWA(R)
## 
##  Note that it is currently not known e.g. whether
##  CT(GF(2)[x]) = RCWA(GF(2)[x]) or not.
##
InstallMethod( IsSubset,
               "for CT(R) and RCWA(R) (RCWA)", ReturnTrue,
               [ IsNaturalCT, IsNaturalRCWA ], SUM_FLAGS,

  function ( CT_R, RCWA_R )
    if FamilyObj(One(CT_R)) <> FamilyObj(One(RCWA_R)) then return false; fi;
    if IsRcwaGroupOverZOrZ_pi(RCWA_R) then return false; fi;
    if   not IsTrivial(Units(CoefficientsRing(Source(One(RCWA_R)))))
    then return false; fi;
    Error("sorry - this is still an open question!");
    return fail;
  end );

#############################################################################
##
#S  Counting / enumerating certain elements of RCWA(R) and CT(R). ///////////
##
#############################################################################

#############################################################################
##
#V  NrElementsOfCTZWithGivenModulus
##
##  The numbers of elements of CT(Z) of given order m <= 24, subject to the
##  conjecture that CT(Z) is the setwise stabilizer of N_0 in RCWA(Z).
##
BindGlobal( "NrElementsOfCTZWithGivenModulus",
[ 1, 1, 17, 238, 4679, 115181, 3482639, 124225680, 5114793582, 238618996919, 
  12441866975999, 716985401817362, 45251629386163199, 3104281120750130159, 
  229987931693135611303, 18301127616460012222080, 1556718246822087917567999, 
  140958365897067252175843218, 13537012873511994353270783999, 
  1374314160482820530097944198162, 147065220260069766956421116517343, 
  16544413778663040175990602280223999, 1951982126641242370890486633922559999, 
  241014406219744996673035312579811441520 ] );

#############################################################################
## 
#F  AllElementsOfCTZWithGivenModulus( m ) .  elements of CT(Z) with modulus m
##
##  Assumes the conjecture that CT(Z) is the setwise stabilizer of the
##  nonnegative integers in RCWA(Z).
##
InstallGlobalFunction( AllElementsOfCTZWithGivenModulus,

  function ( m )

    local  elems, source, ranges, range, perms, g;

    if not IsPosInt(m) then
      Error("usage: AllElementsOfCTZWithGivenModulus( <m> ) ",
            "for a positive integer m\n");
    fi;
    if m = 1 then return [ IdentityRcwaMappingOfZ ]; fi;
    source := AllResidueClassesModulo(Integers,m);
    ranges := PartitionsIntoResidueClasses(Integers,m);
    perms  := AsList(SymmetricGroup(m));
    elems  := [];
    for range in ranges do
      for g in perms do
        Add(elems,RcwaMapping(source,Permuted(range,g)));
      od;
    od;
    return Filtered(Set(elems),elm->Mod(elm)=m);
  end );

#############################################################################
##
#S  Factoring elm's of RCWA(R) into class shifts/reflections/transpositions.
##
#############################################################################

#############################################################################
##
#M  Factorization( RCWA( <R> ), <g> ) .  for RCWA( R ) and an element thereof
##
InstallMethod( Factorization,
               "into class shifts / reflections / transpositions (RCWA)",
               IsCollsElms, [ IsNaturalRCWA, IsRcwaMapping ], 0,
               function(RCWA_R,g) return FactorizationIntoCSCRCT(g); end );

#############################################################################
##
#M  Factorization( CT( <R> ), <g> ) . . .  for CT( R ) and an element thereof
##
InstallMethod( Factorization,
               "into class shifts / reflections / transpositions (RCWA)",
               IsCollsElms, [ IsNaturalCT, IsRcwaMapping ], 0,

 function( CT_R, g )
   if g in CT_R then return FactorizationIntoCSCRCT(g);
                else return fail; fi;
 end );

#############################################################################
##
#S  Decomposing elements of CT(Z). //////////////////////////////////////////
##
#############################################################################

#############################################################################
## 
#A  DecompositionIntoPermutationalAndOrderPreservingElement( <g> )
##
InstallMethod( DecompositionIntoPermutationalAndOrderPreservingElement,
               "for elements of CT(Z) (RCWA)", true, [ IsRcwaMappingOfZ ], 0,

  function ( g )

  local  a, b, cls, P, q;

    if   not IsBijective(g) or not IsSignPreserving(g)
    then TryNextMethod(); fi;

    q := Product(PrimeSet(g));
    cls := AllResidueClassesModulo(Mod(g));
    SortParallel(List(cls,cl->CoefficientsQadic(Residue(cl),q)),cls);
    P := cls^g;
    SortParallel(List(P,cl->CoefficientsQadic(Residue(cl),q)),P);
    b := RcwaMapping(cls,P);
    a := g/b;
    return [ a, b ];
  end );

############################################################################
##
#S  The epimorphisms RCWA(Z) -> C2 and RCWA+(Z) -> (Z,+). ///////////////////
##
#############################################################################

#############################################################################
##
#M  Sign( <f> ) . . . . . . . . . . . for rcwa mappings of Z in standard rep.
##
InstallMethod( Sign,
               "for rcwa mappings of Z in standard rep. (RCWA)",
               true, [ IsRcwaMappingOfZInStandardRep ], 0,

  function ( f )

    local  m, c, sgn, r, ar, br, cr;

    if not IsBijective(f) then return fail; fi;
    m := Modulus(f); c := Coefficients(f);
    sgn := 0;
    for r in [0..m-1] do
      ar := c[r+1][1]; br := c[r+1][2]; cr := c[r+1][3];
      sgn := sgn + br/AbsInt(ar);
      if ar < 0 then sgn := sgn + (m - 2*r); fi;
    od;
    sgn := (-1)^(sgn/m);
    return sgn;
  end );

#############################################################################
##
#M  Sign( <f> ) . . . . . . . . . . . . for rcwa mappings of Z in sparse rep.
##
InstallMethod( Sign,
               "for rcwa mappings of Z in sparse rep. (RCWA)",
               true, [ IsRcwaMappingOfZInSparseRep ], 0,

  function ( f )

    local  sgn, c, r, m, a, b;

    if not IsBijective(f) then return fail; fi;
    sgn := 0;
    for c in f!.coeffs do
      r := c[1]; m := c[2]; a := c[3]; b := c[4];
      sgn := sgn + b/(m*AbsInt(a));
      if a < 0 then sgn := sgn + (m - 2*r)/m; fi;
    od;
    sgn := (-1)^sgn;
    return sgn;
  end );

#############################################################################
##
#M  Determinant( <f> ) . . . . . . .  for rcwa mappings of Z in standard rep.
##
InstallMethod( Determinant,
               "for rcwa mappings of Z in standard rep. (RCWA)",
               true, [ IsRcwaMappingOfZInStandardRep ], 0,

  function ( f )
    if Mult(f) = 0 then return fail; fi;
    return Sum(List(Coefficients(f),c->c[2]/AbsInt(c[1])))/Modulus(f);
  end );

#############################################################################
##
#M  Determinant( <f> ) . . . . . . . .  for rcwa mappings of Z in sparse rep.
##
InstallMethod( Determinant,
               "for rcwa mappings of Z in sparse rep. (RCWA)",
               true, [ IsRcwaMappingOfZInSparseRep ], 0,

  function ( f )
    if Mult(f) = 0 then return fail; fi;
    return Sum(List(Coefficients(f),c->c[4]/AbsInt(c[3]*c[2])));
  end );

#############################################################################
##
#M  Determinant( <f>, <S> ) .  for rcwa mappings on unions of residue classes
##
InstallOtherMethod( Determinant,
                    "for rcwa mappings on unions of residue classes (RCWA)",
                    true, [ IsRcwaMappingOfZInStandardRep,
                            IsResidueClassUnionOfZ ], 0,

  function ( f, S )

    local  m, c, r, cl;

    m := Modulus(f); c := Coefficients(f);
    return Sum(List([1..m],
                    r->Density(Intersection(S,ResidueClass(Integers,m,r-1)))
                      *c[r][2]/AbsInt(c[r][1])));
  end );

#############################################################################
##
#M  Determinant( <f>, <S> ) .  for rcwa mappings on unions of residue classes
##
InstallOtherMethod( Determinant,
                    "for rcwa mappings on unions of residue classes (RCWA)",
                    true, [ IsRcwaMappingOfZInSparseRep,
                            IsResidueClassUnionOfZ ], 0,

  function ( f, S )
    return Sum(List(f!.coeffs,
                    c -> Density(Intersection(S,ResidueClass(c[1],c[2])))
                       * c[4]/AbsInt(c[3])));
  end );

#############################################################################
##
#M  SignInOddCTPZ( <g> ) . . . . . . . .  for elements of CT_P(Z), 2 \notin P
##
InstallMethod( SignInOddCTPZ,
               "for elements of CT_P(Z) where P does not contain 2 (RCWA)",
               true, [ IsRcwaMappingOfZ ], 0,

  function ( g )

    local  factors;

    if   not IsBijective(g) or not IsSignPreserving(g) or 2 in PrimeSet(g)
    then TryNextMethod(); fi;
    factors := DecompositionIntoPermutationalAndOrderPreservingElement(g);
    return SignPerm(Permutation(factors[1],AllResidueClassesModulo(Mod(g))));
  end );

#############################################################################
##
#S  Generating random elements of RCWA(R), CT(R) and other rcwa groups. /////
##
#############################################################################

#############################################################################
##
#M  Random( RCWA( Integers ) ) . . . . . . . .  a `random' element of RCWA(Z)
##
InstallMethod( Random,
               "for RCWA(Z) (RCWA)", true, [ IsNaturalRCWA_Z ], SUM_FLAGS,

  function ( RCWA_Z )

    local  result, ClassTranspositions, ClassShifts, ClassReflections,
           maxmodct, maxmodcscr, noct, nocs, nocr, genfactors, classes,
           tame, integralpairs, g, i;

    tame       := ValueOption("IsTame") = true;
    noct       := GetOption("NumberOfCTs",Random([0..2]),IsInt);
    nocs       := GetOption("NumberOfCSs",Random([0..3]),IsInt);
    nocr       := GetOption("NumberOfCRs",Random([0..3]),IsInt);
    maxmodcscr := GetOption("ModulusBoundForCSCRs",6,IsPosInt);
    maxmodct   := GetOption("ModulusBoundForCTs",14,IsPosInt);
    if maxmodct <> CLASS_PAIRS_LARGE[1] then
      MakeReadWriteGlobal("CLASS_PAIRS_LARGE");
      CLASS_PAIRS_LARGE := [maxmodct,ClassPairs(maxmodct)];
      MakeReadOnlyGlobal("CLASS_PAIRS_LARGE");
    fi;
    classes             := Combinations([1..maxmodcscr],2)-1;
    ClassTranspositions := List([1..noct],i->Random(CLASS_PAIRS[2]));
    if   Random([1..4]) = 1 
    then Add(ClassTranspositions,Random(CLASS_PAIRS_LARGE[2])); fi;
    ClassShifts         := List([1..nocs],i->Random(classes));
    ClassReflections    := List([1..nocr],i->Random(classes));
    ClassTranspositions := List(ClassTranspositions,ClassTransposition);
    ClassShifts         := List(ClassShifts,t->ClassShift(t)^Random([-1,1]));
    ClassReflections    := List(ClassReflections,ClassReflection);
    genfactors          := Concatenation(ClassTranspositions,ClassShifts,
                                         ClassReflections);
    result              := Product(genfactors);
    if result = 1 then result := One(RCWA_Z); fi;
    if not tame then SetFactorizationIntoCSCRCT(result,genfactors); fi;
    if tame then
      integralpairs := Filtered(CLASS_PAIRS[2],t->t[2]=t[4]);
      g := One(RCWA_Z);
      for i in [1..Random([1..3])] do
        g := g * ClassTransposition(Random(integralpairs));
      od;
      result := g^result;
      SetIsTame(result,true);
      SetOrder(result,Order(g));
    fi;
    IsBijective(result);
    return result;
  end );

#############################################################################
##
#M  Random( RCWA( <R> ) ) . . . . . . . . . . . a `random' element of RCWA(R)
##
InstallMethod( Random,
               "for RCWA(R) (RCWA)", true, [ IsNaturalRCWA ], 0,

  function ( RCWA_R )
    return Random( CT( Support( RCWA_R ) ) );
  end );

#############################################################################
##
#M  Random( CT( Integers ) ) . . . . . . . . . .  a `random' element of CT(Z)
##
InstallMethod( Random,
               "for CT(Z) (RCWA)", true, [ IsNaturalCT_Z ], SUM_FLAGS,

  function ( CT_Z )

    local  noct;

    noct := GetOption("NumberOfCTs",RootInt(Random([0..125]),3),IsInt);
    return Random(RCWA(Integers):NumberOfCTs:=noct,NumberOfCSs:=0,
                                 NumberOfCRs:=0);
  end );

#############################################################################
##
#M  Random( CT( <R> ) ) . . . . . . . . . . . . . a `random' element of CT(R)
##
InstallMethod( Random,
               "for CT(R) (RCWA)", true, [ IsNaturalCT ], 0,

  function ( CT_R )

    local  result, R, tame, noct, ClassTranspositions,
           classpairs, integralpairs, maxm, x, g, i;

    if   IsNaturalCT_Z(CT_R)  # For CT(Z) there is a better method available.
    then TryNextMethod(); fi;
    R := Support(CT_R);
    tame := ValueOption("IsTame") = true;
    noct := ValueOption("NumberOfCTs");
    if noct = fail then if not tame then noct := RootInt(Random([0..100]),3);
                                    else noct := Random([0..2]); fi; fi;
    if IsZ_pi(R) then
      maxm := Maximum(NoninvertiblePrimes(R));
      maxm := Maximum(maxm,Minimum(NoninvertiblePrimes(R))^2);
      classpairs := List(ClassPairs(R,maxm),
                         t->[ResidueClass(R,t[2],t[1]),
                             ResidueClass(R,t[4],t[3])]);
    elif IsFiniteFieldPolynomialRing(R) then
      x          := IndeterminatesOfPolynomialRing(R)[1];
      classpairs := ClassPairs(R,x^3);
    fi;
    ClassTranspositions := List([1..noct],i->Random(classpairs));
    ClassTranspositions := List(ClassTranspositions,ClassTransposition);
    result              := Product(ClassTranspositions);
    if result = 1 then result := One(CT_R); fi;
    if   not tame
    then SetFactorizationIntoCSCRCT(result,ClassTranspositions); fi;
    if tame then
      integralpairs := Filtered(classpairs,t->t[2]=t[4]);
      g := One(CT_R);
      for i in [1..Random([1..3])] do
        g := g * ClassTransposition(Random(integralpairs));
      od;
      result := g^result;
      SetIsTame(result,true);
      SetOrder(result,Order(g));
    fi;
    IsBijective(result);
    return result;
  end );

#############################################################################
##
#M  Random( <G>, <n> ) . .  for finitely generated rcwa group and word length
##
InstallOtherMethod( Random,
                    "for rcwa group and word length (RCWA)",
                    ReturnTrue, [ IsRcwaGroup, IsPosInt ], 0,

  function ( G, n )

    local  gens, g, gi, last_gi, i;

    if   not HasGeneratorsOfGroup(G) or Length(GeneratorsOfGroup(G)) <= 1
    then TryNextMethod(); fi;
    if   ForAll(GeneratorsOfGroup(G),g->HasIsClassTransposition(g)
                                    and IsClassTransposition(g))
    then gens := GeneratorsOfGroup(G);
    else gens := Set(GeneratorsAndInverses(G)); fi;
    g := One(G); last_gi := One(G);
    for i in [1..n] do
      repeat gi := Random(gens); until gi <> last_gi^-1;
      g := g * gi;
      last_gi := gi;
    od;
    return g;
  end );

#############################################################################
##
#S  The action of RCWA(R) and CT(R) on R. ///////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  RepresentativeActionOp( RCWA( <R> ), <n1>, <n2>, <act> )
##
InstallMethod( RepresentativeActionOp,
               "for RCWA(Z) and two integers (RCWA)", ReturnTrue,
               [ IsNaturalRCWA, IsInt, IsInt, IsFunction ], 0,

  function ( RCWA_R, n1, n2, act )

    local  R;

    if act <> OnPoints then TryNextMethod(); fi;
    R := Support(RCWA_R);
    if   Characteristic(R) = 0
    then return ClassShift(R)^(n2-n1);
    else return RcwaMapping(R,One(R),[[One(R),n2-n1,One(R)]]); fi;
  end );

#############################################################################
##
#M  RepresentativeActionOp( RCWA(Integers), <l1>, <l2>, <act> )
##
InstallMethod( RepresentativeActionOp,
               "for RCWA(Z) and two k-tuples of integers (RCWA)", ReturnTrue,
               [ IsNaturalRCWA_Z, IsList, IsList, IsFunction ], 0,

  function ( RCWA_Z, l1, l2, act )

    local  g, range, perm, exp, points, n;

    points := Union(l1,l2);
    if not IsSubset(Integers,points) then return fail; fi;
    range := [1..Maximum(points)-Minimum(points)+1]; n := Length(range);
    exp := -Minimum(points)+1;
    perm := RepresentativeAction(SymmetricGroup(n),l1+exp,l2+exp,act);
    if perm = fail then return fail; fi;
    g := RcwaMapping(perm,range);
    return g^(ClassShift(0,1)^-exp);
  end );

#############################################################################
##
#M  RepresentativeActionOp( RCWA( <R> ), <S1>, <S2>, <act> ) 
##
##  Returns an rcwa mapping <g> of <R> which maps <S1> to <S2>.
##  The sets <S1> and <S2> must be unions of residue classes of <R>.
##  The argument <act> is ignored.
##
InstallMethod( RepresentativeActionOp,
               "for RCWA(R) and two residue class unions (RCWA)",
               ReturnTrue,
               [ IsNaturalRCWA, IsResidueClassUnion,
                 IsResidueClassUnion, IsFunction ], 0,

  function ( RCWA_R, S1, S2, act )

        local  Refine, Refinement, R, S, C, g;

    Refinement := function ( cls, lng )

      local  m, splitcl, parts;

      while Length(cls) <> lng do
        m       := Minimum(List(cls,Modulus));
        splitcl := First(cls,cl->Modulus(cl)=m); RemoveSet(cls,splitcl);
        parts := SplittedClass(splitcl,SizeOfSmallestResidueClassRing(R));
        if Length(parts) > lng - Length(cls) + 1 then return fail; fi;
        cls := Union(cls,parts);
      od;
      return cls;
    end;

    Refine := function ( S )
      if   Length(S[1]) > Length(S[2])
      then S[2] := Refinement(S[2],Length(S[1]));
      elif Length(S[2]) > Length(S[1])
      then S[1] := Refinement(S[1],Length(S[2])); fi;
    end;

    R := Support(RCWA_R);
    if not IsSubset(R,S1) or not IsSubset(R,S2) then return fail; fi;
    S := List([S1,S2],AsUnionOfFewClasses);
    C := List([S1,S2],Si->AsUnionOfFewClasses(Difference(R,Si)));
    Refine(S); Refine(C);
    if fail in S or fail in C then return fail; fi;
    g := RcwaMapping(Concatenation(S[1],C[1]),Concatenation(S[2],C[2]));
    return g;
  end );

#############################################################################
##
#M  RepresentativeActionOp( CT( <R> ), <S1>, <S2>, <act> ) 
##
##  Returns an element <g> of CT(<R>) which maps <S1> to <S2>.
##  The sets <S1> and <S2> must be unions of residue classes of <R>.
##  The argument <act> is ignored.
##
InstallMethod( RepresentativeActionOp,
               "for CT(R) and two residue class unions (RCWA)",
               ReturnTrue,
               [ IsNaturalCT, IsResidueClassUnion,
                 IsResidueClassUnion, IsFunction ], 0,

  function ( CT_R, S1, S2, act )

        local  Refine, Refinement, R, S, D1, D2, length, factors, g;

    Refinement := function ( cls, lng )

      local  m, splitcl, parts;

      while Length(cls) <> lng do
        m       := Minimum(List(cls,Modulus));
        splitcl := First(cls,cl->Modulus(cl)=m); RemoveSet(cls,splitcl);
        parts := SplittedClass(splitcl,SizeOfSmallestResidueClassRing(R));
        if Length(parts) > lng - Length(cls) + 1 then return fail; fi;
        cls := Union(cls,parts);
      od;
      return cls;
    end;

    Refine := function ( S )

      local  maxlength, i;

      maxlength := Maximum(List(S,Length));
      for i in [1..Length(S)] do
        if   Length(S[i]) < maxlength
        then S[i] := Refinement(S[i],maxlength); fi;
      od;
    end;

    R := Support(CT_R);
    if not IsSubset(R,S1) or not IsSubset(R,S2) then return fail; fi;

    if IsSubset(S2,S1) then D1 := Difference(S2,S1);
                       else D1 := Difference(R,S1); fi;
    D2 := Difference(R,Union(D1,S2));

    S := List([S1,D1,D2,S2],AsUnionOfFewClasses);
    Refine(S); if fail in S then return fail; fi;
    length := Length(S[1]);

    factors := List([1..3],
                    i->List([1..length],
                            j->ClassTransposition(S[i][j],S[i+1][j])));
    factors := Flat(factors);

    g := Product(Flat(factors));
    SetFactorizationIntoCSCRCT(g,factors);

    return g;
  end );

#############################################################################
##
#M  RepresentativeActionOp( RCWA( Integers ), <P1>, <P2>, <act> ) 
##
##  Returns an rcwa mapping <g> which maps the partition <P1> to the
##  partition <P2> and which is
##
##  - affine on the elements of <P1>, if the option `IsTame' is not set
##    and all elements of both partitions <P1> and <P2> are single residue
##    classes, and
##
##  - tame, if the option `IsTame' is set.
##
##  The arguments <P1> and <P2> must be partitions of Z into equally many
##  disjoint residue class unions, and the argument <act> is ignored.
##
InstallMethod( RepresentativeActionOp,
               "for RCWA(Z) and two class partitions (RCWA)", true,
               [ IsNaturalRCWA_Z, IsList, IsList, IsFunction ], 100,

  function ( RCWA_Z, P1, P2, act )

    local  SplitClass, g, tame, m, c, P, min, minpos, Phat, Buckets, b,
           k, ri, mi, rtildei, mtildei, ar, br, cr, r, i, j;

    SplitClass := function ( i, j )

      local  mods, pos, m, r, mtilde, r1, r2, j2, pos2;

      mods := List(Buckets[i][j],Modulus);
      m    := Minimum(mods);
      pos  := Position(mods,m);
      r    := Residues(Buckets[i][j][pos])[1];
      mtilde := 2*m; r1 := r; r2 := r + m;
      Buckets[i][j] := Union(Difference(Buckets[i][j],[Buckets[i][j][pos]]),
                             [ResidueClass(Integers,mtilde,r1),
                              ResidueClass(Integers,mtilde,r2)]);
      j2  := Filtered([1..k],
                      j->Position(Buckets[3-i][j],
                                  ResidueClass(Integers,m,r))<>fail)[1];
      pos2 := Position(Buckets[3-i][j2],ResidueClass(Integers,m,r));
      Buckets[3-i][j2] := Union(Difference( Buckets[3-i][j2],
                                           [Buckets[3-i][j2][pos2]]),
                                [ResidueClass(Integers,mtilde,r1),
                                 ResidueClass(Integers,mtilde,r2)]);
    end;

    if   Length(P1) <> Length(P2)
      or not ForAll(P1,IsResidueClassUnionOfZ)
      or not ForAll(P2,IsResidueClassUnionOfZ)
      or [Union(List(P1,IncludedElements)),Union(List(P1,ExcludedElements)),
          Union(List(P2,IncludedElements)),Union(List(P2,ExcludedElements))]
         <> [[],[],[],[]]
      or Sum(List(P1,Density)) <> 1 or Sum(List(P2,Density)) <> 1
      or Union(P1) <> Integers or Union(P2) <> Integers
    then TryNextMethod(); fi;

    if not ForAll(P1,IsResidueClass) or not ForAll(P2,IsResidueClass) then
      P1 := List(P1,AsUnionOfFewClasses);
      P2 := List(P2,AsUnionOfFewClasses);
      P  := [P1,P2];
      for j in [1..Length(P1)] do
        if Length(P[1][j]) <> Length(P[2][j]) then
          if Length(P[1][j]) < Length(P[2][j]) then i := 1; else i := 2; fi;
          repeat
            min    := Minimum(List(P[i][j],Modulus));
            minpos := Position(List(P[i][j],Modulus),min);
            P[i][j][minpos] := SplittedClass(P[i][j][minpos],2);
            P[i][j] := Flat(P[i][j]);
          until Length(P[1][j]) = Length(P[2][j]);
        fi;
      od;
      P1 := Flat(P[1]); P2 := Flat(P[2]);
    fi;

    if ValueOption("IsTame") <> true then
      g := RcwaMapping(P1,P2);
    else
      k       := Length(P1);
      m       := Lcm(List(Union(P1,P2),Modulus));
      P       := [P1,P2];
      Phat    := List([0..m-1],r->ResidueClass(Integers,m,r));
      Buckets := List([1..2],i->List([1..k],j->Filtered(Phat,
                 cl->Residues(cl)[1] mod Modulus(P[i][j]) =
                 Residues(P[i][j])[1])));
      repeat
        for i in [1..k] do
          b := [Buckets[1][i],Buckets[2][i]];
          if Length(b[2]) > Length(b[1]) then
            for j in [1..Length(b[2])-Length(b[1])] do
              SplitClass(1,i);
            od;
          elif Length(b[1]) > Length(b[2]) then
            for j in [1..Length(b[1])-Length(b[2])] do
              SplitClass(2,i);
            od;
          fi;
        od;
      until ForAll([1..k],i->Length(Buckets[1][i]) = Length(Buckets[2][i]));
      g := RcwaMapping(Flat(Buckets[1]),Flat(Buckets[2]));
      SetIsTame(g,true); SetRespectedPartition(g,Set(Flat(Buckets[1])));
    fi;
    return g;
  end );

#############################################################################
##
#M  RepresentativeActionOp( RCWA( <R> ), <P1>, <P2>, <act> ) 
##
##  Returns an rcwa mapping <g> which maps the partition <P1> to the
##  partition <P2> and which is affine on the elements of <P1>.
##
##  The arguments <P1> and <P2> must be partitions of <R> into equally
##  many residue classes, and the argument <act> is ignored.
##
InstallMethod( RepresentativeActionOp,
               "for RCWA(<R>) and two class partitions (RCWA)", true,
               [ IsNaturalRCWA, IsList, IsList, IsFunction ], 0,

  function ( RCWA_R, P1, P2, act )

    local  R, g;

    R := Support(RCWA_R);

    if   Length(P1) <> Length(P2)
      or not ForAll(Union(P1,P2),IsResidueClass)
      or not ForAll(Union(P1,P2),cl->IsSubset(R,cl))
      or not ForAll([P1,P2],P->Sum(List(P,Density))=1)
      or not ForAll([P1,P2],P->Union(P) = R)
    then TryNextMethod(); fi;

    return RcwaMapping(P1,P2);
  end );

#############################################################################
##
#S  Conjugacy of elements in RCWA(R) and CT(R). /////////////////////////////
##
#############################################################################

#############################################################################
##
#M  IsConjugate( RCWA( Integers ), <f>, <g> ) 
##
##  This method checks whether the `standard conjugates' of <f> and <g> are
##  equal, if the mappings are tame, and looks for different lengths of short
##  cycles otherwise. The latter will often not terminate. In particular this
##  is the case if <f> and <g> are conjugate.
##
InstallMethod( IsConjugate,
               "for two rcwa mappings of Z, in RCWA(Z) (RCWA)",
               true, [ IsNaturalRCWA_Z, IsRcwaMappingOfZ,
                       IsRcwaMappingOfZ ], 0,

  function ( RCWA_Z, f, g )

    local  maxlng;

    if f = g then return true; fi;
    if Order(f) <> Order(g) or IsTame(f) <> IsTame(g) then return false; fi;
    if IsTame(f) then
      return StandardConjugate(f) = StandardConjugate(g);
    else
      maxlng := 2;
      repeat
        if    Collected(List(ShortCycles(f,maxlng),Length))
           <> Collected(List(ShortCycles(g,maxlng),Length))
        then return false; fi;
        maxlng := maxlng * 2;
      until false;
    fi;
  end );

#############################################################################
##
#M  IsConjugate( RCWA( Z_pi( <pi> ) ), <f>, <g> ) 
##
##  This method makes only trivial checks. It cannot confirm conjugacy
##  unless <f> and <g> are equal.
##
InstallMethod( IsConjugate,
               "for two rcwa mappings of Z_(pi) in RCWA(Z_(pi)) (RCWA)",
               ReturnTrue, [ IsNaturalRCWA_Z_pi,
                             IsRcwaMappingOfZ_pi, IsRcwaMappingOfZ_pi ], 0,

  function ( RCWA_Z_pi, f, g )

    local  maxlng;

    if not f in RCWA_Z_pi or not g in RCWA_Z_pi then return fail; fi;
    if f = g then return true; fi;
    if Order(f) <> Order(g) or IsTame(f) <> IsTame(g) then return false; fi;
    maxlng := 2;
    repeat
      if    Collected(List(ShortCycles(f,maxlng),Length))
         <> Collected(List(ShortCycles(g,maxlng),Length))
      then return false; fi;
      maxlng := maxlng * 2;
    until false;
  end );

#############################################################################
##
#M  RepresentativeActionOp( RCWA( Integers ), <f>, <g>, <act> ) 
##
##  This method is for tame rcwa mappings of Z under the conjugation action
##  of RCWA(Z). It returns an rcwa mapping <h> such that <f>^<h> = <g> in
##  case such a mapping exists and fail otherwise. The action <act> must be
##  `OnPoints'.
##
InstallMethod( RepresentativeActionOp,
               "for RCWA(Z) and two tame rcwa mappings of Z (RCWA)", true,
               [ IsNaturalRCWA_Z, IsRcwaMappingOfZ, IsRcwaMappingOfZ,
                 IsFunction ], 0,

  function ( RCWA_Z, f, g, act )
    if act <> OnPoints then TryNextMethod(); fi;
    if f = g then return One(f); fi;
    if not ForAll([f,g],IsTame) then TryNextMethod(); fi;
    if Order(f) <> Order(g) then return fail; fi;
    if StandardConjugate(f) <> StandardConjugate(g) then return fail; fi;
    return StandardizingConjugator(f) * StandardizingConjugator(g)^-1;
  end );

#############################################################################
##
#M  RepresentativeActionOp( RCWA( Integers ), <f>, <g>, <act> ) 
##
##  This method is for tame rcwa mappings of Z under the conjugation action
##  of RCWA(Z). It covers the special case of products of the same numbers of
##  class shifts, inverses of class shifts, class reflections and class
##  transpositions, each, whose supports are pairwise disjoint and do not
##  entirely cover Z up to a finite complement.
##
InstallMethod( RepresentativeActionOp,
               "for RCWA(Z) and products of disjoint CS/CR/CT (RCWA)", true,
               [ IsNaturalRCWA_Z, IsRcwaMappingOfZ, IsRcwaMappingOfZ,
                 IsFunction ], 100,

  function ( RCWA_Z, f, g, act )

    local  RefinedPartition, Sorted, maps, facts, P, l, sigma, i;

    RefinedPartition := function ( P, k )

      local  l, mods, min, pos;

      P := ShallowCopy(P); l := Length(P);
      while l < k do
        mods   := List(P,Modulus);
        min    := Minimum(mods);
        pos    := Position(mods,min);
        P[pos] := SplittedClass(P[pos],2);
        P      := Flat(P);
        l      := l + 1;
      od;
      return P;
    end;

    Sorted := l -> [Filtered(l,fact->IsClassShift(fact)
                                  or IsClassShift(fact^-1)),
                    Filtered(l,IsClassReflection),
                    Filtered(l,IsClassTransposition)];              

    if act <> OnPoints then TryNextMethod(); fi;
    if f = g then return One(f); fi;
    if not ForAll([f,g],IsTame) then TryNextMethod(); fi;
    if Order(f) <> Order(g) then return fail; fi;
    maps  := [f,g];
    facts := List(maps,FactorizationIntoCSCRCT);
    if   Length(facts[1]) <> Length(facts[2])
      or 1 in List(maps,map->Density(Support(map)))
      or ForAny([1..2],i->Density(Support(maps[i]))
                       <> Sum(List(facts[i],fact->Density(Support(fact)))))
      or not ForAll(Union(facts),
                    fact ->   IsClassShift(fact) or IsClassShift(fact^-1)
                           or IsClassReflection(fact)
                           or IsClassTransposition(fact))
    then TryNextMethod(); fi;
    facts := List(facts,Sorted);
    if   List(facts[1],Length) <> List(facts[2],Length)
    then TryNextMethod(); fi;
    P := List(maps,
              map->AsUnionOfFewClasses(Difference(Integers,Support(map))));
    l  := Maximum(List(P,Length));
    for i in [1..2] do
      if l > Length(P[i]) then P[i] := RefinedPartition(P[i],l); fi;
    od;
    for i in [1..2] do
      Append(P[i],Flat(List(Flat(facts[i]),
                  fact->Filtered(LargestSourcesOfAffineMappings(fact),
                                 cl->Intersection(Support(fact),cl)<>[]))));
    od;
    sigma := RcwaMapping(P[1],P[2]);
    for i in [1..Length(facts[1][1])] do
      if   Number([facts[1][1][i]^-1,facts[2][1][i]^-1],IsClassShift) = 1
      then sigma := ClassReflection(Support(facts[1][1][i])) * sigma; fi;
    od;
    if f^sigma <> g then Error("`RepresentativeAction' failed.\n"); fi;
    return sigma;
  end );

#############################################################################
##
#M  RepresentativeActionOp( CT( <R> ), <ct1>, <ct2>, <act> ) 
##
##  This is a special method for two class transpositions whose supports have
##  nontrivial complements, under the conjugation action of CT(<R>).
##  The factorization of the result into 6 class transpositions is stored.
##  The existence of such products is used in the proof that the group which
##  is generated by all class transpositions of Z is simple.
##
InstallMethod( RepresentativeActionOp,
               "for CT(R) and two class transpositions (RCWA)", ReturnTrue,
               [ IsNaturalCT, IsClassTransposition, IsClassTransposition,
                 IsFunction ], 50,

  function ( G, ct1, ct2, act )

    local  R, result, sixcts, cls, D, supd, pieces, factors, d, two;

    R := Support(G);

    if act <> OnPoints or Source(ct1) <> R or Source(ct2) <> R
      or R in List([ct1,ct2],Support)
    then TryNextMethod(); fi;

    if   IsRing(R) then two := 2;
    elif IsZxZ(R)  then two := [1,2];
    else TryNextMethod(); fi;

    cls  := Concatenation(List([ct1,ct2],TransposedClasses));
    supd := 1 - Sum(List(cls{[3,4]},Density));
    D := AsUnionOfFewClasses(Difference(R,Union(cls{[1,2]})))[1];
    pieces := Int(Density(D)/supd)+1;
    if   IsZ_pi(R) then
      while not IsSubset(NoninvertiblePrimes(R),Factors(pieces)) do
        pieces := pieces + 1;
      od;
    elif IsFiniteFieldPolynomialRing(R) then
      while SmallestRootInt(pieces) <> Characteristic(R) do
        pieces := pieces + 1;
      od;
    elif IsZxZ(R) then
      d := RootInt(pieces,2);
      repeat
        factors := [d,pieces/d];
        d := d - 1;
      until ForAll(factors,IsInt);
      pieces := factors;
    fi;
    D := SplittedClass(D,pieces)[1];
    Append(cls,SplittedClass(D,two));
    D := AsUnionOfFewClasses(Difference(R,Union(cls{[3..6]})));
    if Length(D) = 1 then D := SplittedClass(D[1],two); fi;
    Append(cls,D{[1,2]});
    sixcts := List([cls{[1,5]},cls{[2,6]},cls{[5,7]},
                    cls{[6,8]},cls{[3,7]},cls{[4,8]}],ClassTransposition);
    result := Product(sixcts);
    SetFactorizationIntoCSCRCT(result,sixcts);
    return result;
  end );

#############################################################################
##
#F  NrConjugacyClassesOfRCWAZOfOrder( <ord> ) . #Ccl of RCWA(Z) / order <ord>
##
InstallGlobalFunction( NrConjugacyClassesOfRCWAZOfOrder,

  function ( ord )
    if   not IsPosInt(ord) then return 0;
    elif ord = 1 then return 1;
    elif ord mod 2 = 0 then return infinity;
    else return Length(Filtered(Combinations(DivisorsInt(ord)),
                                l -> l <> [] and Lcm(l) = ord));
    fi;
  end );

#############################################################################
##
#F  NrConjugacyClassesOfCTZOfOrder( <ord> ) . . . #Ccl of CT(Z) / order <ord>
##
InstallGlobalFunction( NrConjugacyClassesOfCTZOfOrder,

  function ( ord )
    if   not IsPosInt(ord) then return 0;
    elif ord = 1 then return 1;
    else Info(InfoWarning,1,"Function `NrConjugacyClassesOfCTZOfOrder' ",
                            "assumes the conjecture ");
         Info(InfoWarning,1,"that CT(Z) is the setwise ",
                            "stabilizer of N_0 in RCWA(Z).");
         return Length(Filtered(Combinations(DivisorsInt(ord)),
                                l -> l <> [] and Lcm(l) = ord));
    fi;
  end );

#############################################################################
##
#S  Conjugacy of subgroups in RCWA(R) and CT(R). ////////////////////////////
##
#############################################################################

#############################################################################
##
#M  RepresentativeActionOp( RCWA(Integers), <G>, <H>, <act> )
##
InstallMethod( RepresentativeActionOp,
               "for RCWA(Z) and two rcwa groups over Z (RCWA)", ReturnTrue,
               [ IsNaturalRCWA_Z, IsRcwaGroupOverZ, IsRcwaGroupOverZ,
                 IsFunction ], 0,

  function ( RCWA_Z, G, H, act )

    local  PG, PH, Gp, Hp, suppG, suppH, fixG, fixH, d, i, j, m, g, perm,
           dontdelegate;

    if act <> OnPoints then TryNextMethod(); fi;   
    if   (Density(Support(G))  = 1 and Density(Support(H)) <> 1)
      or (Density(Support(G)) <> 1 and Density(Support(H))  = 1)
    then return fail; fi;
    if IsTame(G) <> IsTame(H) or Size(G) <> Size(H) then return fail; fi;

    dontdelegate := ValueOption("dontdelegate") = true;

    if IsTame(G) then
      PG := RespectedPartition(G);
      PH := RespectedPartition(H);
      if IsIntegers(Support(G)) and Length(PG) <> Length(PH) then
        if dontdelegate then
          return "gave up: supp(G) = supp(H) = Z and |P_G| <> |P_H|";
        fi;
        TryNextMethod();
      fi;
      suppG := Filtered(PG,cl->IsSubset(Support(G),cl));
      suppH := Filtered(PH,cl->IsSubset(Support(H),cl));
      if Length(suppG) <> Length(suppH) then
        if dontdelegate then
          return "gave up: #classes in supp(G) and supp(H) are distinct";
        fi;
        TryNextMethod();
      fi;
      d := Length(PG) - Length(PH);
      if d < 0 then
        fixG := Difference(PG,suppG);
        for i in [1..-d] do
          m := Minimum(List(fixG,Mod));
          j := First([1..Length(fixG)],j->Mod(fixG[j])=m);
          fixG[j] := SplittedClass(fixG[j],2);
          fixG := Set(Flat(fixG)); 
        od;
        PG := Concatenation(suppG,fixG);
      elif d > 0 then
        fixH := Difference(PH,suppH);
        for i in [1..d] do
          m := Minimum(List(fixH,Mod));
          j := First([1..Length(fixH)],j->Mod(fixH[j])=m);
          fixH[j] := SplittedClass(fixH[j],2);
          fixH := Set(Flat(fixH)); 
        od;
        PH := Concatenation(suppH,fixH);
      fi;
      Gp := Action(G,PG);
      Hp := Action(H,PH);
      perm := RepresentativeAction(SymmetricGroup(Length(PG)),
                                   Gp,Hp,OnPoints);
      if perm = fail then
        if dontdelegate then
          return "gave up: actions of G on P_G and H on P_H not conjugate";
        fi;
        TryNextMethod();
      fi;
      g := RcwaMapping(PG,Permuted(PH,perm^-1));
      if IsSignPreserving(G) and IsSignPreserving(H) then return g; fi;
      if G^g = H then return g; else
        if dontdelegate then
          return "gave up: conjugation test failed: G^g <> H";
        fi;
        TryNextMethod();
      fi;
    else
      if dontdelegate then return "gave up: both groups are wild"; fi;
      TryNextMethod();
    fi;
  end );

#############################################################################
##
#S  Constructing rcwa groups: ///////////////////////////////////////////////
#S  The general things. /////////////////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  IsomorphismRcwaGroup( <G>, <R> ) . . . . . . . . . general wrapper method
##
InstallMethod( IsomorphismRcwaGroup,
               "general wrapper method (RCWA)", true,
               [ IsGroup, IsRing ], 0,

  function ( G, R )

    local  phi, phi1, phi2, H, gensG, gensH, pi;

    if   IsIntegers(R) and HasIsomorphismRcwaGroupOverZ(G)
    then return IsomorphismRcwaGroupOverZ(G); fi;
    if IsFinite(G) then TryNextMethod(); fi;  # Handled in a separate method.
    if IsRcwaGroupOverZ(G) and IsZ_pi(R) then # Use "1 to 1" translation.
      gensG := GeneratorsOfGroup(G);
      pi    := NoninvertiblePrimes(R);
      if   not ForAll(gensG,g->IsSubset(pi,Factors(Modulus(g))))
      then TryNextMethod(); fi;
      gensH := List(gensG,g->SemilocalizedRcwaMapping(g,pi));
      if not IsGeneratorsOfMagmaWithInverses(gensH) then TryNextMethod(); fi;
      H     := Group(gensH);
      phi   := GroupHomomorphismByImagesNC(G,H);
      SetIsBijective(phi,true);
      return phi;
    fi;
    if not IsRcwaGroup(G) and IsZ_pi(R) then  # Use rcwa group over Z as an
      phi1 := IsomorphismRcwaGroupOverZ(G);   # intermediate step.
      phi2 := IsomorphismRcwaGroup(Image(phi1),R);
      phi  := CompositionMapping(phi2,phi1);
      return phi;
    fi;
    TryNextMethod();
  end );

#############################################################################
##
#M  IsomorphismRcwaGroup( <G> ) . . . one-argument method, use Z as base ring
##
InstallMethod( IsomorphismRcwaGroup,
               "one-argument method, use Z as base ring (RCWA)", true,
               [ IsGroup ], 0, G -> IsomorphismRcwaGroup( G, Integers ) );

#############################################################################
##
#M  IsomorphismRcwaGroupOverZ( <G> ) . . . dispatch to `IsomorphismRcwaGroup'
##
InstallMethod( IsomorphismRcwaGroupOverZ,
               "dispatch to `IsomorphismRcwaGroup' (RCWA)", true,
               [ IsGroup ], 0, IsomorphismRcwaGroup );

#############################################################################
##
#S  Constructing rcwa groups: ///////////////////////////////////////////////
#S  Tame groups (cyclic, dihedral, finitely generated abelian, finite). /////
##
#############################################################################

#############################################################################
##
#M  CyclicGroupCons( IsRcwaGroupOverZ, <n> )
##
InstallMethod( CyclicGroupCons,
               "rcwa group over Z, for a positive integer (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsPosInt ], 0,

  function ( filter, n )

    local  result;

    if n = 1 then return TrivialRcwaGroupOverZ; fi;
    result := Image(IsomorphismRcwaGroupOverZ(CyclicGroup(IsPermGroup,n)));
    SetSize(result,n); SetIsCyclic(result,true);
    SetIsTame(result,true); SetIsIntegral(result,true);
    return result;
  end );

#############################################################################
##
#M  CyclicGroupCons( IsRcwaGroupOverZ, infinity )
##
InstallOtherMethod( CyclicGroupCons,
                    "(Z,+) as an rcwa group (RCWA)", ReturnTrue,
                    [ IsRcwaGroupOverZ, IsInfinity ], 0,

  function ( filter, infty )

    local  result;

    result := Group(ClassShift(0,1));
    SetSize(result,infinity); SetIsCyclic(result,true);
    SetIsTame(result,true); SetIsIntegral(result,true);
    return result;
  end );

#############################################################################
##
#M  DihedralGroupCons( IsRcwaGroupOverZ, infinity )
##
InstallOtherMethod( DihedralGroupCons,
                    "the rcwa group < n |-> n+1, n |-> -n > (RCWA)",
                    ReturnTrue, [ IsRcwaGroupOverZ, IsInfinity ], 0,

  function ( filter, infty )

    local  result;

    result := Group(ClassShift(0,1),ClassReflection(0,1));
    SetSize(result,infinity);
    SetIsTame(result,true); SetIsIntegral(result,true);
    return result;
  end );

#############################################################################
##
#M  DihedralGroupCons( IsRcwaGroup, <cl> ) . . . . . . .  for a residue class
##
InstallOtherMethod( DihedralGroupCons,
                    "for a residue class (RCWA)",
                    ReturnTrue, [ IsRcwaGroup, IsResidueClass ], 0,

  function ( filter, cl )

    local  result;

    result := Group(ClassShift(cl),ClassReflection(cl));
    SetIsTame(result,true); SetIsIntegral(result,true);
    return result;
  end );

#############################################################################
##
#M  DihedralGroupCons( IsPcGroup, <cl> ) . . . . . . . .  for a residue class
##
##  This is a dirty hack to make DihedralGroup( <cl> ) work.
##
InstallOtherMethod( DihedralGroupCons,
                    "for a residue class (RCWA)",
                    ReturnTrue, [ IsPcGroup, IsResidueClass ], 0,

  function ( filter, cl )

    local  result;

    result := Group(ClassShift(cl),ClassReflection(cl));
    SetIsTame(result,true); SetIsIntegral(result,true);
    return result;
  end );

#############################################################################
##
#M  SymmetricGroupCons( IsRcwaGroup, <P> ) for a part. of a ring into res.cl.
##
InstallOtherMethod( SymmetricGroupCons,
                    "for a partition of a ring into residue classes (RCWA)",
                    ReturnTrue, [ IsRcwaGroup, IsList ], 0,

  function ( filter, P )

    local  Sym, g;

    if    ForAll(P,IsResidueClass)
      and Length(Set(List(P,cl->UnderlyingRing(FamilyObj(cl))))) = 1
    then
      Sym := Group(RcwaMapping([P]),RcwaMapping([P{[1,2]}]));
      SetSize(Sym,Factorial(Length(P)));
      SetIsTame(Sym,true);
      SetRespectedPartition(Sym,P);
      return Sym;
    else TryNextMethod(); fi;
  end );

#############################################################################
##
#M  SymmetricGroupCons( IsPermGroup, <P> ) for a part. of a ring into res.cl.
##
##  This is a dirty hack to make SymmetricGroup( <P> ) work.
##
InstallOtherMethod( SymmetricGroupCons,
                    "for a partition of a ring into residue classes (RCWA)",
                    ReturnTrue, [ IsPermGroup, IsList ], SUM_FLAGS,

  function ( filter, P )
    if   not IsEmpty(P) and ForAll(P,IsResidueClass)
    then return SymmetricGroupCons(IsRcwaGroup,P);
    else TryNextMethod(); fi;
  end );

#############################################################################
##
#M  AbelianGroupCons( IsRcwaGroupOverZ, <invs> )
##
InstallMethod( AbelianGroupCons,
               "rcwa group over Z, for list of abelian inv's (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsList ], 0,

  function ( filter, invs )

    local  result;

    if not ForAll(invs,n->IsInt(n) or n=infinity) then TryNextMethod(); fi;
    result := DirectProduct(List(invs,n->CyclicGroup(IsRcwaGroupOverZ,n)));
    SetSize(result,Product(invs)); SetIsAbelian(result,true);
    SetIsTame(result,true); SetIsIntegral(result,true);
    return result;
  end );

#############################################################################
##
#M  IsomorphismRcwaGroup( <gl>, <cl> )  GL(2,Z) on given residue class of Z^2
##
InstallMethod( IsomorphismRcwaGroup,
               "GL(2,Z) on a given residue class of Z^2 (RCWA)", ReturnTrue,
               [ IsIntegerMatrixGroup and IsNaturalGL, IsResidueClassOfZxZ ],
               0,

  function ( gl, cl )

    local  phi, img;

    if DimensionOfMatrixGroup(gl) <> 2 then TryNextMethod(); fi;

    img := Group(List(GeneratorsOfGroup(gl),gen->ClassRotation(cl,gen)));
    phi := GroupHomomorphismByImagesNC(gl,img);

    SetIsBijective(phi,true);
    SetFilterObj(phi,IsNaturalRcwaRepresentationOfGLOrSL);

    return phi;
  end );

#############################################################################
##
#M  IsomorphismRcwaGroup( <sl>, <cl> )  SL(2,Z) on given residue class of Z^2
##
InstallMethod( IsomorphismRcwaGroup,
               "SL(2,Z) on a given residue class of Z^2 (RCWA)", ReturnTrue,
               [ IsIntegerMatrixGroup and IsNaturalSL, IsResidueClassOfZxZ ],
               0,

  function ( sl, cl )

    local  phi, img;

    if DimensionOfMatrixGroup(sl) <> 2 then TryNextMethod(); fi;

    img := Group(List(GeneratorsOfGroup(sl),gen->ClassRotation(cl,gen)));
    phi := GroupHomomorphismByImagesNC(sl,img);

    SetIsBijective(phi,true);
    SetFilterObj(phi,IsNaturalRcwaRepresentationOfGLOrSL);

    return phi;
  end );

#############################################################################
##
#M  ImagesRepresentative( <phi>, <mat> )  for rcwa rep. of SL(2,Z) or GL(2,Z)
##
InstallMethod( ImagesRepresentative,
               "for rcwa rep. of SL(2,Z) or GL(2,Z) over Z^2 (RCWA)",
               ReturnTrue, [ IsNaturalRcwaRepresentationOfGLOrSL, IsMatrix ],
               0,

  function ( phi, mat )

    local  G, H;

    G := Source(phi); H := Range(phi);

    if   DimensionOfMatrixGroup(G) <> 2 or not IsRcwaGroupOverZxZ(H)
    then TryNextMethod(); fi;

    if not mat in G then return fail; fi;

    return ClassRotation(Support(H),mat);
  end );

#############################################################################
##
#M  IsomorphismRcwaGroup( <G>, <R> ) . . . . default method for finite groups
##
InstallMethod( IsomorphismRcwaGroup,
               "default method for finite groups (RCWA)", true,
               [ IsGroup and IsFinite, IsRing ], 0,

  function ( G, R )

    local  H, Hgens, phi1, phi2, phi, Gperm, n, P, p, pos, maxdensity;

    if   IsRcwaGroup(G) and Source(One(G)) = R
    then return IdentityMapping(G); fi;
    if IsTrivial(G) then
      return GroupHomomorphismByImagesNC(G,TrivialSubgroup(RCWA(R)),
               [One(G)],GeneratorsOfGroup(TrivialSubgroup(RCWA(R))));
    fi;
    if   not IsPermGroup(G) 
    then phi1 := IsomorphismPermGroup(G); Gperm := Image(phi1);
    else phi1 := IdentityMapping(G);      Gperm := G; fi;
    n := LargestMovedPoint(Gperm);
    if IsIntegers(R) then P := AllResidueClassesModulo(Integers,n); else
      p := SizeOfSmallestResidueClassRing(R);
      P := [R];
      while Length(P) < n do
        maxdensity := Maximum(List(P,Density));
        pos := First([1..Length(P)], i -> Density(P[i]) = maxdensity);
        P[pos] := SplittedClass(P[pos],p);
        P := Flat(P);
      od;
      P := Set(P);
    fi;
    Hgens := List(GeneratorsOfGroup(Gperm),
                  g->RcwaMapping(List(Cycles(g,[1..n]),cyc->P{cyc})));
    H    := GroupWithGenerators(Hgens);
    SetIsTame(H,true);
    SetRespectedPartition(H,P);
    phi2 := GroupHomomorphismByImagesNC(Gperm,H);
    phi := Immutable(CompositionMapping(phi2,phi1));
    SetIsInjective(phi,true); SetIsSurjective(phi,true);
    if HasSize(G) then SetSize(Image(phi),Size(G)); fi;
    return phi;
  end );

#############################################################################
##
#S  Constructing rcwa groups: ///////////////////////////////////////////////
#S  Free groups and free products of finite groups. /////////////////////////
##
#############################################################################

#############################################################################
##
#M  IsomorphismRcwaGroup( <F>, Integers ) . . . . . . for free groups, over Z
##
##  This method uses an adaptation of the construction given on page 27
##  of the book Pierre de la Harpe: Topics in Geometric Group Theory from
##  PSL(2,C) to RCWA(Z).
##
InstallMethod( IsomorphismRcwaGroup,
               "for free groups, over Z (RCWA)", ReturnTrue,
               [ IsFreeGroup, IsIntegers ], 0,

  function ( F, ints )

    local  rank, m, D, gamma, RCWA_Z, phi, image, i;

    rank := Length(GeneratorsOfGroup(F));
    if rank = 1 then
      phi := GroupHomomorphismByImagesNC(F,Group(ClassShift(0,1)),
                                         [F.1],[ClassShift(0,1)]);
      SetSize(Image(phi),infinity); SetIsTame(Image(phi),true);
      SetIsBijective(phi,true);
      return phi;
    fi;
    m      := 2 * rank;
    D      := AllResidueClassesModulo(m);
    RCWA_Z := RCWA(Integers);
    gamma  := List([1..rank],
                   k->RepresentativeAction(RCWA_Z,
                                           Difference(Integers,D[2*k-1]),
                                           D[2*k]));
    for i in [1..rank] do
      SetIsTame(gamma[i],false); SetOrder(gamma[i],infinity);
    od;
    image := Group(gamma);
    SetSize(image,infinity); SetIsTame(image,false);
    SetRankOfFreeGroup(image,rank);
    phi := GroupHomomorphismByImagesNC(F,image);
    SetIsBijective(phi,true);
    return phi;
  end );

#############################################################################
##
#M  IsomorphismRcwaGroup( <F>, Integers )  for free products of finite groups
##
##  This method uses the Table-Tennis Lemma -- see e.g. Section II.B. in
##  the book Pierre de la Harpe: Topics in Geometric Group Theory.
##
##  The method uses regular permutation representations of the factors G_r
##  (r = 0..m-1) of the free product on residue classes modulo n_r := |G_r|.
##
##  The basic idea is that since point stabilizers in regular permutation
##  groups are trivial, all non-identity elements map any of the permuted
##  residue classes into their complements.
##
##  To get into a situation where the Table-Tennis Lemma is applicable,
##  the method computes conjugates of the images of the mentioned permutation
##  representations under bijective rcwa mappings g_r which satisfy
##  0(n_r)^g_r = Z \ r(m).
##
InstallMethod( IsomorphismRcwaGroup,
               "for free products of finite groups, over Z (RCWA)",
               ReturnTrue, [ IsFpGroup and HasFreeProductInfo,
                             IsIntegers ], 0,

  function ( F, ints )

    local  phi, img, gens, gensF, info, groups, degs, embs, embsF, embnrs,
           regreps, rcwareps, conjisos, conjelms, r, m, i;

    info   := FreeProductInfo(F);
    groups := info.groups;
    m      := Length(groups);
    degs   := List(groups,Size);
    if   m < 2 or degs = [2,2] # We cannot use the Table-Tennis L. for C2*C2.
      or ForAny(groups,IsTrivial) or not ForAll(groups,IsFinite) 
    then TryNextMethod(); fi;
    regreps  := List(groups,RegularActionHomomorphism);
    rcwareps := List(regreps,phi->IsomorphismRcwaGroupOverZ(Image(phi)));
    conjelms := List([0..m-1],r->RepresentativeAction(RCWA(Integers),
                                 ResidueClass(0,degs[r+1]),
                                 Difference(Integers,ResidueClass(r,m))));
    conjisos := List([1..m],i->ConjugatorIsomorphism(Image(rcwareps[i]),
                                                     conjelms[i]));
    embs     := List([1..m],i->CompositionMapping(conjisos[i],rcwareps[i],
                                                  regreps[i]));
    gensF    := GeneratorsOfGroup(F);
    embsF    := info.embeddings;
    embnrs   := List(gensF,
                     f->First([1..m],
                              i->ForAny(GeneratorsOfGroup(Image(embsF[i])),
                                        g->IsIdenticalObj(f,g))));
    gens     := List([1..Length(gensF)],
                     i->Image(embs[embnrs[i]],
                              PreImagesRepresentativeNC(embsF[embnrs[i]],
                                                        gensF[i])));
    img := Group(gens);
    SetSize(img,infinity); SetIsTame(img,false);
    SetFreeProductInfo(img,info);
    phi := GroupHomomorphismByImagesNC(F,img);
    SetIsBijective(phi,true);
    return phi;
  end );

#############################################################################
##
#M  IsomorphismRcwaGroup( <F>, Integers ) .  for the free product of two C2's
##
##  This method covers the case that the free product is C2 * C2.
##
InstallMethod( IsomorphismRcwaGroup,
               "for free products of cyclic groups of order 2 (RCWA)",
               ReturnTrue, [ IsFpGroup and HasFreeProductInfo,
                             IsIntegers ], 0,

  function ( F, ints )

    local  phi, img, gens, info, groups, degs;

    info   := FreeProductInfo(F);
    groups := Filtered(info.groups,IsNonTrivial);
    degs   := List(groups,Size);
    if degs <> [2,2] then TryNextMethod(); fi;
    gens := [ClassReflection(0,1),ClassReflection(0,1)*ClassShift(0,1)];
    img  := Group(gens);
    SetIsTame(img,true); SetSize(img,infinity);
    SetFreeProductInfo(img,info);
    phi := GroupHomomorphismByImagesNC(F,img);
    SetIsBijective(phi,true);
    return phi;
  end );

#############################################################################
##
#S  Constructing rcwa groups: ///////////////////////////////////////////////
#S  Restriction monomorphisms and induction epimorphisms. ///////////////////
##
#############################################################################

#############################################################################
##
#M  Restriction( <G>, <f> ) . . . . . . . . . . . . . . . . . for rcwa groups
##
InstallMethod( Restriction,
               "for rcwa groups (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsRcwaMapping ], 0,

  function ( G, f )

    local Gf;

    if   Source(One(G)) <> Source(f) or not IsInjective(f)
    then return fail; fi;

    Gf := GroupByGenerators(List(GeneratorsOfGroup(G),g->Restriction(g,f)));

    if HasIsTame(G) then SetIsTame(Gf,IsTame(G)); fi;
    if HasSize(G)   then SetSize(Gf,Size(G)); fi;

    return Gf;
  end );

#############################################################################
##
#M  Induction( <G>, <f> ) . . . . . . . . . . . . . . . . . . for rcwa groups
##
InstallMethod( Induction,
               "for rcwa groups (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsRcwaMapping ], 0,

  function ( G, f )

    local Gf;

    if Source(One(G)) <> Source(f) or not IsInjective(f)
      or not IsSubset(Image(f),MovedPoints(G))
    then return fail; fi;

    Gf := GroupByGenerators(List(GeneratorsOfGroup(G),g->Induction(g,f)));

    if HasIsTame(G) then SetIsTame(Gf,IsTame(G)); fi;
    if HasSize(G)   then SetSize(Gf,Size(G)); fi;

    return Gf;
  end );

#############################################################################
##
#S  The automorphism switching action on negative and nonnegative integers. /
##
#############################################################################

#############################################################################
##
#M  Mirrored( <G> ) . . . . . . . . . . . . . . . . .  for rcwa groups over Z
##
InstallOtherMethod( Mirrored,
                    "for rcwa groups over Z (RCWA)",
                    true, [ IsRcwaGroupOverZ ], 0,

  function ( G )

    local  img;

    img := GroupByGenerators(List(GeneratorsOfGroup(G),Mirrored));
    if HasIsTame(G) then SetIsTame(img,IsTame(G)); fi;
    if HasSize(G)   then SetSize(img,Size(G)); fi;
    return img;
  end );

#############################################################################
##
#S  Constructing rcwa groups: ///////////////////////////////////////////////
#S  Taking direct products and wreath products. /////////////////////////////
##
#############################################################################

#############################################################################
##
#M  DirectProductOp( <groups>, <onegroup> ) . . . . .  for rcwa groups over Z
##
InstallMethod( DirectProductOp,
               "for rcwa groups over Z (RCWA)", ReturnTrue,
               [ IsList, IsRcwaGroupOverZ ], 0,

  function ( groups, onegroup )

    local  D, factors, f, m, G, gens, info, first;

    if   IsEmpty(groups) or not ForAll(groups,IsRcwaGroupOverZ)
    then TryNextMethod(); fi;
    m := Length(groups);
    f := List([0..m-1],r->RcwaMapping([[m,r,1]]));
    factors := List([1..m],r->Restriction(groups[r],f[r]));
    gens := []; first := [1];
    for G in factors do
      gens := Concatenation(gens,GeneratorsOfGroup(G));
      Add(first,Length(gens)+1);
    od;

    D := GroupByGenerators(gens);
    info := rec(groups := groups, first := first,
                embeddings := [ ], projections := [ ]);
    SetDirectProductInfo(D,info);

    if   ForAll(groups,HasIsTame) and ForAll(groups,IsTame)
    then SetIsTame(D,true); fi;
    if   ForAny(groups,grp->HasIsTame(grp) and not IsTame(grp))
    then SetIsTame(D,false); fi;
    if   ForAny(groups,grp->HasSize(grp) and not IsFinite(grp))
    then SetSize(D,infinity); fi;
    if   ForAll(groups,grp->HasSize(grp) and IsInt(Size(grp)))
    then SetSize(D,Product(List(groups,Size))); fi;

    return D;
  end );

#############################################################################
##
#M  WreathProduct( <G>, <P> ) .  for rcwa group over Z and finite perm.-group
##
InstallMethod( WreathProduct,
               "for an rcwa group over Z and a finite perm.-group (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsPermGroup ], 0,

  function ( G, P )

    local  prod, info, m, orbreps, gensonreps, blocks, base, h, nrgensG;

    nrgensG    := Length(GeneratorsOfGroup(G));
    m          := NrMovedPoints(P);
    orbreps    := List(Orbits(P,MovedPoints(P)),Representative);
    gensonreps := Concatenation(List(orbreps,r->GeneratorsOfGroup(
                                Restriction(G,RcwaMapping([[m,r-1,1]])))));
    blocks     := AllResidueClassesModulo(m);
    base       := DirectProduct(List([0..m-1],r->G));
    h          := Image(IsomorphismRcwaGroup(P,Integers));
    prod       := Group(Concatenation(gensonreps,GeneratorsOfGroup(h)));
    info       := rec( groups := [ G, P ], alpha := IdentityMapping(P),
                       base := base, basegens := GeneratorsOfGroup(base),
                       I := P, degI := m, hgens := GeneratorsOfGroup(h),
                       components := blocks,
                       embeddings := [ GroupHomomorphismByImagesNC( G, prod,
                                         GeneratorsOfGroup(G),
                                         gensonreps{[1..nrgensG]} ),
                                       GroupHomomorphismByImagesNC( P, prod,
                                         GeneratorsOfGroup(P), ~.hgens ) ] );
    SetWreathProductInfo(prod,info);
    if HasIsTame(G) then
      if IsTame(G) then
        SetIsTame(prod,true);
        if HasRespectedPartition(G) then
          SetRespectedPartition(prod,Union(List([0..m-1],
                                     r->m*RespectedPartition(G)+r)));
        fi;
      else SetIsTame(prod,false); fi;
    fi;
    if HasSize(G) then
      if IsFinite(G) then SetSize(prod,Size(G)^m*Size(P));
                     else SetSize(prod,infinity); fi;
    fi;
    return prod;
  end );

#############################################################################
##
#M  WreathProduct( <G>, <Z> ) . . . . . .  for an rcwa group over Z and (Z,+)
##
InstallMethod( WreathProduct,
               "for an rcwa group over Z and an infinite cyclic gp. (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsGroup and IsCyclic ], 0,

  function ( G, Z )

    local  prod, info, Gpi, z;

    if   Length(GeneratorsOfGroup(Z)) <> 1 or IsFinite(Z)
    then TryNextMethod(); fi;
    Gpi := Restriction(G,RcwaMapping([[4,3,1]]));
    z   := ClassTransposition(0,2,1,2) * ClassTransposition(0,2,1,4);
    SetIsTame(z,false); SetOrder(z,infinity);
    prod := Group(Concatenation(GeneratorsOfGroup(Gpi),[z]));
    info := rec( groups := [ G, Z ], alpha := IdentityMapping(Z),
                 I := Z, degI := infinity, hgens := [ z ],
                 embeddings := [ GroupHomomorphismByImagesNC( G, prod,
                                   GeneratorsOfGroup(G),
                                   GeneratorsOfGroup(Gpi)),
                                 GroupHomomorphismByImagesNC( Z, prod,
                                   GeneratorsOfGroup(Z), [z] ) ] );
    SetWreathProductInfo(prod,info);
    SetIsTame(prod,false); SetSize(prod,infinity);
    return prod;
  end );

#############################################################################
##
#M  WreathProduct( <G>, <F2> ) . . . . . . .  for an rcwa group over Z and F2
##
##  This method needs a correction:
##  The generators f1 and f2 need to be adjusted in such a way that the group
##  generated by them is not only free of rank 2, but acts also regularly on
##  a partition of Z into residue classes -- provided that this is possible!
##
InstallMethod( WreathProduct,
               "for an rcwa group over Z and a free group of rank 2 (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsGroup ], 0,

  function ( G, F2 )

    local  prod, info, Gpi, f1, f2;

    if ValueOption("RCWADevel") <> true then TryNextMethod(); fi;

    if not IsFreeGroup(F2) and not HasRankOfFreeGroup(F2)
      or RankOfFreeGroup(F2) <> 2 or Length(GeneratorsOfGroup(F2)) <> 2
    then TryNextMethod(); fi;

    Gpi := Restriction(G,RcwaMapping([[24,2,1]]));
    f1  := ClassTransposition(2,4,3,4) * ClassTransposition(4,6,8,12)
         * ClassTransposition(3,4,4,6);
    f2  := ClassTransposition(0,2,1,2) * ClassTransposition(1,2,2,4);
    SetIsTame(f1,false); SetIsTame(f2,false);
    prod := Group(Concatenation(GeneratorsOfGroup(Gpi),[f1,f2]));
    info := rec( groups := [ G, F2 ], alpha := IdentityMapping(F2),
                 I := F2, degI := infinity, hgens := [ f1, f2 ],
                 embeddings := [ GroupHomomorphismByImagesNC( G, prod,
                                   GeneratorsOfGroup(G),
                                   GeneratorsOfGroup(Gpi)),
                                 GroupHomomorphismByImagesNC( F2, prod,
                                   GeneratorsOfGroup(F2), [ f1, f2 ] ) ] );
    SetWreathProductInfo(prod,info);
    SetIsTame(prod,false); SetSize(prod,infinity);

    return prod;
  end );

#############################################################################
##
#M  Embedding( <W>, <i> ) . . . . . . . . . . for a wreath product and 1 or 2
##
InstallMethod( Embedding,
               "for a wreath product and 1 or 2 (RCWA)",
               ReturnTrue, [ IsGroup and HasWreathProductInfo, IsPosInt ], 0,

  function ( W, i )
    local info;
    info := WreathProductInfo(W);
    if IsBound(info.embeddings) and IsBound(info.embeddings[i]) then
      return info.embeddings[i];
    fi;
    TryNextMethod();
  end );

#############################################################################
##
#S  Constructing rcwa groups: ///////////////////////////////////////////////
#S  Group extensions. ///////////////////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#O  MergerExtension( <G>, <points>, <point> ) . . . . build rcwa group over Z
##
InstallMethod( MergerExtension,
               "build rcwa group over Z (RCWA)",
               ReturnTrue, [ IsPermGroup, IsList, IsPosInt ],

  function ( G, points, point )

    local  P, S1, S2, g, n;

    n := LargestMovedPoint(G);
    if   not IsSubset([1..n],points) or point > n or point in points
    then return fail; fi;

    P  := AllResidueClassesModulo(Integers,n);
    S1 := P{points};
    S2 := SplittedClass(P[point],Length(points));
    g  := Product([1..Length(points)],i->ClassTransposition(S1[i],S2[i]));
    return ClosureGroupAddElm(Image(IsomorphismRcwaGroupOverZ(G)),g); 
  end );

#############################################################################
##
#S  Constructing rcwa groups: ///////////////////////////////////////////////
#S  Getting smaller or otherwise nicer sets of generators. //////////////////
##
#############################################################################

#############################################################################
##
#M  SmallGeneratingSet( <G> ) . . . . . . . . . . . .  for rcwa groups over Z
##
InstallMethod( SmallGeneratingSet,
               "for rcwa groups over Z (RCWA)",
               true, [ IsRcwaGroupOverZ ], 0,

  function ( G )

    local  gens, gensred, H, r, modulusbound, i, shrinked;

    if IsTrivial(G) then return [One(G)]; fi;

    gens         := Set(Filtered(GeneratorsOfGroup(G),g->not IsOne(g)));
    modulusbound := Lcm(List(gens,Modulus));

    if ForAll(gens,IsClassTransposition) then
      SortParallel(List(gens,ct->[Modulus(ct),TransposedClasses(ct)]),gens);
    fi;

    Info(InfoRCWA,1,"SmallGeneratingSet: #initial generators = ",
                    Length(gens));
    Info(InfoRCWA,2,"Initial generators = ",gens);

    r := 1;
    repeat
      gensred := gens{[1]};
      for i in [2..Length(gens)] do
        Info(InfoRCWA,3,"i = ",i);
        H := Group(gens{[1..i-1]});
        if   IsEmpty(Intersection(RestrictedBall(H,One(H),r,modulusbound),
                                  RestrictedBall(H,gens[i],r,modulusbound)))
        then Add(gensred,gens[i]); fi;
      od;
      Info(InfoRCWA,1,"SmallGeneratingSet: #generators after reduction ",
                      "with r = ",r,": ",Length(gensred));
      Info(InfoRCWA,2,"Remaining generators = ",gensred);

      shrinked := Length(gensred) < Length(gens);
      gens     := gensred;
      r        := r + 1;
    until not shrinked;    

    return gens;
  end );

#############################################################################
##
#S  Constructing rcwa groups: Other. ////////////////////////////////////////
##
#############################################################################

InstallGlobalFunction( GroupByResidueClasses,

  classes -> Group( List( Filtered( Combinations( classes, 2 ),
                                    cls -> IsEmpty( Intersection( cls ) ) ),
                          ClassTransposition ) ) );

#############################################################################
##
#S  Iterators for rcwa groups. //////////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  Iterator( <G> ) . . . .  for rcwa groups (and infinite groups in general)
##
InstallMethod( Iterator,
               "for rcwa groups (and infinite groups in general) (RCWA)",
               true, [ IsGroup ], 0,

  function ( G )
    if   not IsRcwaGroup(G) and HasIsFinite(G) and IsFinite(G)
    then TryNextMethod(); fi;
    return Objectify( NewType( IteratorsFamily, IsIterator
                                            and IsMutable
                                            and IsRcwaGroupsIteratorRep ),
                      rec( G         := G,
                           sphere    := [One(G)],
                           oldsphere := [],
                           pos       := 1 ) );
  end );

#############################################################################
##
#M  NextIterator( <iter> ) . . . . . . . . . . . for iterators of rcwa groups
##
InstallMethod( NextIterator,
               "for iterators of rcwa groups (RCWA)", true,
               [ IsIterator and IsMutable and IsRcwaGroupsIteratorRep ], 5,

  function ( iter )

    local  G, gens, sphere, g;

    G := iter!.G;
    if not HasGeneratorsOfGroup(G) then TryNextMethod(); fi;
    gens := GeneratorsAndInverses(G);
    g := iter!.sphere[iter!.pos];
    if iter!.pos < Length(iter!.sphere) then iter!.pos := iter!.pos + 1; else
      sphere := Difference(Union(List(gens,g->iter!.sphere*g)),
                           Union(iter!.sphere,iter!.oldsphere));
      iter!.oldsphere := iter!.sphere;
      iter!.sphere    := sphere;
      iter!.pos       := 1;
    fi;
    return g;
  end );

#############################################################################
##
#M  NextIterator( <iter> ) . .  for iterators of rcwa groups, by parent group
##
InstallMethod( NextIterator,
               "for iterators of rcwa groups, by parent group (RCWA)", true,
               [ IsIterator and IsMutable and IsRcwaGroupsIteratorRep ], 0,

  function ( iter )

    local  G, P, gens, sphere, g;

    G := iter!.G;
    if   not HasParent(G) or not HasElementTestFunction(G)
    then TryNextMethod(); fi;
    P := Parent(G);
    if not HasGeneratorsOfGroup(P) then TryNextMethod(); fi;
    gens := GeneratorsAndInverses(P);
    g := iter!.sphere[iter!.pos];
    repeat
      iter!.pos := iter!.pos + 1;
    until    iter!.pos > Length(iter!.sphere)
          or ElementTestFunction(G)(iter!.sphere[iter!.pos]) = true;
    while iter!.pos > Length(iter!.sphere) do
      sphere := Difference(Union(List(gens,g->iter!.sphere*g)),
                           Union(iter!.sphere,iter!.oldsphere));
      iter!.oldsphere := iter!.sphere;
      iter!.sphere    := sphere;
      iter!.pos       := 1;
      repeat
        iter!.pos := iter!.pos + 1;
      until    iter!.pos > Length(iter!.sphere)
            or ElementTestFunction(G)(iter!.sphere[iter!.pos]) = true;
    od;
    return g;
  end );

#############################################################################
##
#M  IsDoneIterator( <iter> ) . . . . . . . . . . for iterators of rcwa groups
##
InstallMethod( IsDoneIterator,
               "for iterators of rcwa groups (RCWA)", true,
               [ IsIterator and IsRcwaGroupsIteratorRep ], 0,
               iter -> IsEmpty( iter!.sphere ) );

#############################################################################
##
#M  ShallowCopy( <iter> ) . . . . . . . . . . .  for iterators of rcwa groups
##
InstallMethod( ShallowCopy,
               "for iterators of rcwa groups (RCWA)", true,
               [ IsIterator and IsRcwaGroupsIteratorRep ], 0,

  iter -> Objectify( Subtype( TypeObj( iter ), IsMutable ),
                     rec( G         := iter!.G,
                          sphere    := iter!.sphere,
                          oldsphere := iter!.oldsphere,
                          pos       := iter!.pos ) ) );

#############################################################################
##
#M  ViewObj( <iter> ) . . . . . . . . . . . . .  for iterators of rcwa groups
##
InstallMethod( ViewObj,
               "for iterators of rcwa groups (RCWA)", true,
               [ IsIterator and IsRcwaGroupsIteratorRep ], 0,

  function ( iter )
    Print("Iterator of ");
    ViewObj(iter!.G);
  end );

#############################################################################
##
#S  The support of an rcwa group. ///////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  MovedPoints( <G> ) . . . . . . . . . . . . . . . . . . .  for rcwa groups
##
##  Returns the set of moved points (i.e. the support) of the rcwa group <G>.
##
InstallMethod( MovedPoints,
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )
    if IsNaturalRCWA(G) or IsNaturalCT(G) then return Source(One(G)); fi;
    return Union(List(GeneratorsOfGroup(G),MovedPoints));
  end );

#############################################################################
##
#S  Orbits of rcwa groups on the set of residue classes (mod m). ////////////
##
#############################################################################

#############################################################################
##
#M  OrbitsModulo( <G>, <m> ) . . . . . . . . . . . . . . . .  for rcwa groups
##
InstallMethod( OrbitsModulo,
               "for rcwa groups (RCWA)", true,
               [ IsRcwaGroup, IsRingElement ], 0,

  function ( G, m )

    local  result, R, gens, orbit, oldorbit, orbitset, img, remaining;

    R := Source(One(G));
    if not m in R then TryNextMethod(); fi;
    gens      := GeneratorsAndInverses(G);
    remaining := AllResidueClassesModulo(R,m); result := [];
    repeat
      orbit := [remaining[1]];
      repeat
        oldorbit := ShallowCopy(orbit);
        orbitset := Union(orbit);
        img      := Union(List(gens,gen->orbitset^gen));
        orbit    := Union(orbit,
                          Filtered(remaining,cl->Intersection(cl,img)<>[]));
      until orbit = oldorbit;
      Add(result,List(orbit,Residue));
      remaining := Difference(remaining,orbit);
    until remaining = [];
    return result;
  end );

#############################################################################
##
#M  OrbitsModulo( <G>, <m> ) . . . . . . . . for rcwa groups over Z or Z_(pi)
##
InstallMethod( OrbitsModulo,
               "for rcwa groups over Z or Z_(pi) (RCWA)", true,
               [ IsRcwaGroupOverZOrZ_pi, IsPosInt ], 0,

  function ( G, m )

    local  result, orbsgen, orbit, oldorbit, remaining;

    orbsgen := Concatenation(List(GeneratorsOfGroup(G),
                                  g->OrbitsModulo(g,m)));
    remaining := [0..m-1]; result := [];
    repeat
      orbit := [remaining[1]];
      repeat
        oldorbit := ShallowCopy(orbit);
        orbit := Union(orbit,
                       Union(Filtered(orbsgen,
                                      orb->Intersection(orbit,orb)<>[])));
      until orbit = oldorbit;
      Add(result,orbit);
      remaining := Difference(remaining,orbit);
    until remaining = [];
    return result;
  end );

#############################################################################
##
#M  OrbitsModulo( <G>, <m>, <d> ) . . . . . . . . . .  for rcwa groups over Z
##
InstallMethod( OrbitsModulo,
               "for rcwa groups over Z (RCWA)", true,
               [ IsRcwaGroupOverZ, IsPosInt, IsPosInt ], 0,

  function ( G, m, d )

    local  result, gens, orbit, spheres, S_last, S, S_next, remaining,
           Neighbors;

    Neighbors := function ( p, g )

      local  me, pe, e, imgs;

      e    := Mod(g)*Gcd(m,Div(g));
      me   := m * e;
      pe   := List(Tuples([0..e-1],d),t->m*t+p);
      imgs := Set(pe,t->List(t,n->(n^g) mod m));
      return imgs;
    end;

    gens := Set(GeneratorsAndInverses(G));
    remaining := Tuples([0..m-1],d);
    result := [];
    repeat
      S_last  := [];
      S       := [remaining[1]];
      spheres := [];
      repeat
        Add(spheres,S);
        S_next := Union(List(S,p->Union(List(gens,g->Neighbors(p,g)))));
        S_next := Difference(S_next,S_last);
        S_next := Difference(S_next,S);
        S_last := ShallowCopy(S);
        S := ShallowCopy(S_next);
      until S = [];
      orbit := Union(spheres);
      Add(result,orbit);
      remaining := Difference(remaining,orbit);
    until remaining = [];

    return result;
  end );

#############################################################################
##
#M  OrbitsModulo( <G>, <m>, <d>, <trials> ) . . . . .  for rcwa groups over Z
##
##  Stochastic method. -- Accuracy can be increased by increasing <trials>.
##  Returns never less orbits than there actually are.
##
InstallOtherMethod( OrbitsModulo,
                    "for rcwa groups over Z (RCWA)", true,
                    [ IsRcwaGroupOverZ, IsPosInt, IsPosInt, IsPosInt ], 0,

  function ( G, m, d, trials )

    local  gens, factor, trial, points, edges, edge, neighbors,
           orbits, orbit, sphere, lastsphere, nextsphere, rem, g, p, pe, q;

    gens   := Set(GeneratorsAndInverses(G));
    factor := Lcm(List(gens,Mod)) * Lcm(List(gens,Div));
    points := Tuples([0..m-1],d);
    edges  := [];

    for trial in [1..trials] do
      g  := Random(gens);
      p  := Random(points);
      pe := List(p,c->c+Random([0..factor-1])*m);
      q  := List(pe,c->c^g mod m);
      Add(edges,[PositionSorted(points,p),PositionSorted(points,q)]);
    od;

    edges := Set(Filtered(List(edges,Set),e->Length(e)=2));
    neighbors := List([1..m^d],r->[]);
    for edge in edges do
      Add(neighbors[edge[1]],edge[2]);
      Add(neighbors[edge[2]],edge[1]);
    od;

    orbits := [];
    rem    := [1..m^d];
    while rem <> [] do
      lastsphere := [];
      sphere     := [rem[1]];
      orbit      := sphere;
      repeat
        nextsphere := Difference(Union(neighbors{sphere}),sphere);
        nextsphere := Difference(nextsphere,lastsphere);
        lastsphere := sphere;
        sphere     := nextsphere;
        orbit      := Union(orbit,sphere);
      until sphere = [];
      Add(orbits,orbit);
      rem := Difference(rem,orbit);
    od;
    orbits := List(orbits,orbit->points{orbit});

    return orbits;
  end );

#############################################################################
##
#M  ProjectionsToInvariantUnionsOfResidueClasses( <G>, <m> )  for rcwa groups
##
InstallMethod( ProjectionsToInvariantUnionsOfResidueClasses,
               "for rcwa groups (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsRingElement ], 0,

  function ( G, m )

    local  R, orbs, gens, groups;

    R := Source(One(G)); if not m in R then TryNextMethod(); fi;
    gens   := GeneratorsOfGroup(G);
    orbs   := List(OrbitsModulo(G,m),orb->ResidueClassUnion(R,m,orb));
    groups := List(orbs,orb->Group(List(gens,gen->RestrictedPerm(gen,orb))));
    return List(groups,grp->GroupHomomorphismByImagesNC(G,grp));
  end );

#############################################################################
##
#S  Tame rcwa groups: ///////////////////////////////////////////////////////
#S  Testing for tameness and computing respected partitions. ////////////////
##
#############################################################################

#############################################################################
##
#M  CheckForWildness( <G>, <maxlng>, <maxmod> ) . . . . . . . for rcwa groups
##
InstallMethod( CheckForWildness,
               "for rcwa groups (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsPosInt, IsRingElement ], 0,

  function ( G, maxlng, maxmod )

    local  g, gens, gen, lng;

    if ValueOption("AssumeIsTame") = true then return; fi;
    gens := Set(GeneratorsAndInverses(G));
    if Length(gens) <= 1 then return; fi;
    gen := Random(gens);
    g   := gen; lng := 1;
    repeat
      lng := lng + 1;
      gen := Random(Difference(gens,[gen^-1]));
      g := g * gen;
      if Loops(g) <> [] then
        SetIsTame(G,false);
        SetModulusOfRcwaMonoid(G,Zero(Source(One(G))));
        break;
      fi;
    until lng >= maxlng or Mod(g) > maxmod;
  end );

#############################################################################
##
#F  CommonRefinementOfPartitionsOfR_NC( <partitions> ) . . . . . general case
##
InstallGlobalFunction( CommonRefinementOfPartitionsOfR_NC,

  function ( partitions )

    local  table, partition, R, mods, res, m, reps, inds, numreps,
           pow, mj, rj, i, j, k;

    R := partitions[1][1];
    if   not IsRing(R) and not IsZxZ(R)
    then R := UnderlyingRing(FamilyObj(R)); fi;

    mods    := List(partitions,P->List(P,Mod));
    res     := List(partitions,P->List(P,Residues));
    m       := Lcm(R,Concatenation(mods));
    numreps := NumberOfResidues(R,m);
    reps    := AllResidues(R,m);
    table   := List([1..numreps],i->0);
    pow     := 1;
    for i in [1..Length(partitions)] do
      for j in [1..Length(partitions[i])] do
        mj := mods[i][j]; rj := res[i][j];
        for k in [1..numreps] do
          if   reps[k] mod mj in rj
          then table[k] := table[k] + pow; fi;
        od;
        pow := pow + pow;
      od;
    od;

    partition := EquivalenceClasses([1..numreps],k->table[k]);
    return Set(List(partition,inds->ResidueClassUnion(R,m,reps{inds})));
  end );

#############################################################################
##
#F  CommonRefinementOfPartitionsOfZ_NC( <partitions> ) . . special case R = Z
##
InstallGlobalFunction( CommonRefinementOfPartitionsOfZ_NC,

  function ( partitions )

    local  table, partition, mods, res, m,
           pow, mj, r, i, j, k;

    mods  := List(partitions,P->List(P,Mod));
    res   := List(partitions,P->List(P,Residues));
    m     := Lcm(Concatenation(mods));
    table := List([1..m],i->0);
    pow   := 1;
    for i in [1..Length(partitions)] do
      for j in [1..Length(partitions[i])] do
        mj := mods[i][j];
        for r in res[i][j] do
          for k in [r,r+mj..r+(Int(m/mj)-1)*mj] do
            table[k+1] := table[k+1] + pow;
          od;
        od;
        pow := pow + pow;
      od;
    od;
    partition := EquivalenceClasses([1..m],r->table[r]);
    return Set(List(partition,r->ResidueClassUnion(Integers,m,r-1)));
  end );

#############################################################################
##
#M  RefinementSequence( <G>, <maxlng>, <maxparts> ) .  for rcwa groups over Z
##
InstallMethod( RefinementSequence, 
               "for rcwa groups over Z (RCWA)", ReturnTrue,
               [ IsRcwaGroupOverZ, IsPosInt, IsPosInt ], 0,

  function ( G, maxlng, maxparts )

    local  Pk, P, Pimgs, gens, k, starttime, timeout;

    timeout := GetOption("timeout",infinity,IsPosInt);
    starttime := Runtime();
    gens := GeneratorsOfGroup(G);
    if not ForAll(gens,IsTame) then return fail; fi; 
    P  := CommonRefinementOfPartitionsOfZ_NC(List(gens,RespectedPartition));
    Pk := [P];
    while Length(Pk) < maxlng and Length(P) < maxparts do
      Pimgs := Concatenation([P],List(gens,g->P^g));
      P     := CommonRefinementOfPartitionsOfZ_NC(Pimgs);
      Add(Pk,P);
      if Pk[Length(Pk)-1] = P then break; fi;
      if Runtime() - starttime > timeout then break; fi;
    od;
    return Pk;
  end );

#############################################################################
##
#M  Modulus( <G> ) . . . . . . . . . . . . . . . . . . . . .  for rcwa groups
##
InstallMethod( Modulus, 
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )

    local  R, P, m;

    if HasModulusOfRcwaMonoid(G) then return ModulusOfRcwaMonoid(G); fi;

    R := Source(One(G));
    if IsZxZ(R) then R := Integers^[2,2]; fi;

    if IsIntegral(G) then
      Info(InfoRCWA,3,"Modulus: <G> is integral.");
      m := Lcm(R,List(GeneratorsOfGroup(G),Modulus));
      SetModulusOfRcwaMonoid(G,m); return m;
    fi;

    if not HasIsTame(G) then
      CheckForWildness(G,50,Lcm(R,List(GeneratorsOfGroup(G),Modulus))^2);
    fi;

    if   HasIsTame(G) and not IsTame(G)
    then SetModulusOfRcwaMonoid(G,Zero(R)); return Zero(R); fi;

    P := RespectedPartition(G);

    if   P = fail
    then SetModulusOfRcwaMonoid(G,Zero(R)); return Zero(R);
    else m := Lcm(R,List(P,Modulus));
         SetModulusOfRcwaMonoid(G,m);
         return m;
    fi;

  end );

#############################################################################
##
#M  Multiplier( <G> ) . . . . . . . . . . . . . . . . . . . . for rcwa groups
##
InstallMethod( Multiplier,
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )

    local  R, orbs, thin, thick, quot, int;

    R := Source(One(G));
    if not IsTame(G) then return infinity; fi;
    orbs  := Orbits(G,RespectedPartition(G));
    thin  := List(orbs,o->First(o,cl->Density(cl)=Minimum(List(o,Density))));
    thick := List(orbs,o->First(o,cl->Density(cl)=Maximum(List(o,Density))));
    quot  := List([1..Length(orbs)],
                  i->Lcm(Mod(thin[i]),Mod(thick[i]))/Mod(thin[i])
                    *Lcm(Mod(thin[i]),Mod(thick[i]))/Mod(thick[i]));
    int   := List(quot,q->Length(AllResidues(R,q)));
    return quot[Position(int,Maximum(int))];
  end );

#############################################################################
##
#M  Divisor( <G> ) . . . . . . . . . . . . . . . . . . . . .  for rcwa groups
##
InstallMethod( Divisor,
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,
               Multiplier );

#############################################################################
##
#M  RespectedPartition( <G> ) . . . . . . . . . . . . .  for tame rcwa groups
##
InstallMethod( RespectedPartition,
               "for tame rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )

    local  P, P_last, CommonRefinementFunction, gens, start;

    if   IsRcwaGroupOverZ(G)
    then CommonRefinementFunction := CommonRefinementOfPartitionsOfZ_NC;
    else CommonRefinementFunction := CommonRefinementOfPartitionsOfR_NC; fi;

    start := Runtime();

    gens  := GeneratorsOfGroup(G);
    P     := List(gens,LargestSourcesOfAffineMappings);
    P     := CommonRefinementFunction(P);

    repeat
      Info(InfoRCWA,2,"RespectedPartition: |P| = ",Length(P));
      P_last := P;
      P      := Concatenation([P],List(gens,g->P^g));
      P      := CommonRefinementFunction(P);
      if ValueOption("AssumeIsTame") <> true then
        if (Length(P) > 256 and Length(P_last) <= 256)
          or   (IsRcwaGroupOverZ(G) and Maximum(List(P,Mod)) > 16384
            and Maximum(List(P_last,Mod)) <= 16384) then
          CheckForWildness(G,100,Lcm(List(gens,Mod))^3);
          if HasIsTame(G) and not IsTame(G) then return fail; fi;
        fi;
      elif Runtime() - start > 30000 # 30s; to be improved:
      then if Loops(Product(gens)^2) <> [] then return fail; fi;
      fi;
    until P = P_last;

    P := Set(Flat(List(P,AsUnionOfFewClasses)));

    if   RespectsPartition(G,P)
    then SetIsTame(G,true); return P;
    else Error("RespectedPartition failed"); fi; # should never happen
  end );

#############################################################################
##
#M  RespectedPartition( <G> ) . . . . . . . . . for finite subgroups of CT(Z)
##
InstallMethod( RespectedPartition,
               "for finite subgroups of CT(Z) (RCWA)", true,
               [ IsRcwaGroupOverZ ], 5,

  function ( G )

    local  P, orbit, gens, coeffs, compute_moduli, moduli, modulibound,
           primes, primes_multdiv, primes_onlymod, powers_impossible, nc,
           orb, orbitlengthbound, modulusbound, density, m, n, r, i, j, k, b;

    compute_moduli := function (  )
      moduli := AllSmoothIntegers(primes,modulibound);
      moduli := Filtered(moduli,m->ForAll(powers_impossible,q->m mod q<>0));
    end;

    orbit := function ( cl0 )

      local  B, r, cl, img, inter, densitysum, c, i, j;

      B := [[cl0]];
      r := 0;
      densitysum := 1/cl0[2];
      repeat
        r := r + 1;
        Add(B,[]);
        for i in [1..Length(B[r])] do
          cl := B[r][i];
          if   ForAny(P,c->(cl[1]-c[1]) mod Gcd(cl[2],c[2]) = 0)
          then return fail; fi;
          if cl[2] > 10^16 then # finiteness no more plausible
            CheckForWildness(G,1000,Lcm(List(gens,Mod))^4);
            return "abort";
          fi;
          for j in [1..Length(coeffs)] do
            inter := Filtered(coeffs[j],
                              c->(cl[1]-c[1]) mod Gcd(cl[2],c[2]) = 0);
            if    Length(inter) > 1
              and Length(Set(List(inter,c->c{[3..5]}))) > 1
            then return fail; fi;
            c := inter[1];
            img := [(c[3]*cl[1]+c[4])/c[5],c[3]*cl[2]/c[5]];
            img[1] := img[1] mod img[2];
            if img in B[r] or (r > 1 and img in B[r-1]) then continue; fi;
            if img <> cl0 and (img[1]-cl0[1]) mod Gcd(img[2],cl0[2]) = 0 then
              Info(InfoRCWA,2,"RespectedPartition: loop detected for cl0 = ",
                              cl0[1],"(",cl0[2],"), at r = ",r);
              return "loop";
            fi;
            Add(B[r+1],img);
          od;
        od;
        B[r+1] := Set(B[r+1]);
        densitysum := densitysum + Sum(B[r+1],cl->1/cl[2]);
        if densitysum > 1 then
          Info(InfoRCWA,2,"RespectedPartition: density sum exceeded 1 ",
                          "for cl0 = ",cl0[1],"(",cl0[2],"), at r = ",r);
          return "loop";
        fi;
        if not nc and B[r+1] <> [] and Sum(List(B,Length)) > b then
          CheckForWildness(G,500,Lcm(List(gens,Mod))^4);
          if HasIsTame(G) and not IsTame(G) then return "abort"; fi;
        fi;
        if B[r+1] <> [] and Sum(List(B,Length)) > orbitlengthbound then
          if not nc then CheckForWildness(G,1000,Lcm(List(gens,Mod))^4); fi;
          Info(InfoRCWA,2,"RespectedPartition: orbit length for ",
                          "for cl0 = ",cl0[1],"(",cl0[2],") exceeded ",
                          orbitlengthbound," at r = ",r);
          return "abort";
        fi;
      until B[r+1] = [];
      return Concatenation(B);
    end;

    if   ValueOption("classic") = true
      or ValueOption("classic_partition") = true
    then TryNextMethod(); fi;

    orbitlengthbound := GetOption("orbitlengthbound",infinity,IsPosInt);
    modulusbound := GetOption("modulusbound",infinity,IsPosInt);
    nc := ValueOption("NC") = true;

    if IsTrivial(G) then return [ Integers ]; fi;
    if not IsSignPreserving(G) then TryNextMethod(); fi;
    gens := Set(GeneratorsAndInverses(SparseRep(G)));
    coeffs := List(gens,g->ShallowCopy(Coefficients(g)));
    b := Lcm(List(gens,Mod));
    for i in [1..Length(coeffs)] do
      Sort(coeffs[i],function(c1,c2)
                       return c1{[2,1]} < c2{[2,1]};
                     end);
    od;
    primes := PrimeSet(G);
    primes_multdiv := Union(List(gens,g->Set(Factors(Mult(g)*Div(g)))));
    primes_multdiv := Difference(primes_multdiv,[1]);
    primes_onlymod := Difference(primes,primes_multdiv);
    m := Lcm(List(gens,Mod));
    powers_impossible := List(primes_onlymod,p->p^(PValuation(m,p)+1));
    modulibound := 2^20;
    compute_moduli();
    P := List(AsUnionOfFewClasses(Difference(Integers,Support(G))),
              cl->[Residue(cl),Modulus(cl)]);
    repeat
      n := -1;
      repeat
        n := n + 1;
      until ForAll(P,cl->n mod cl[2] <> cl[1]);
      i := 0;
      repeat
        i := i + 1;
        if i > Length(moduli) then
          if modulibound < modulusbound then
            if not nc then
              Error("exceeded modulus bound ",modulibound,
                    ", maybe the group is infinite?\n",
                    "Enter return; to proceed with new bound ",
                    16 * modulibound,".\n");
            fi;
            modulibound := modulibound * 16;
            compute_moduli();
          else
            if nc then return "computation aborted"; fi;
            Info(InfoRCWA,2,"RespectedPartition: exceeded bound ",
                            modulusbound," on the smallest modulus of ",
                            "a residue class in an orbit.");
            CheckForWildness(G,1000,Lcm(List(gens,Mod))^4);
            if HasIsTame(G) and not IsTame(G) then
              SetSize(G,infinity);
              return fail; 
            else return "computation aborted"; fi;
          fi;
        fi;
        m := moduli[i];
        orb := orbit([n mod m,m]);
      until orb <> fail;
      if orb = "loop" then
        SetModulusOfRcwaMonoid(G,0);
        SetSize(G,infinity);
        return fail; 
      fi;
      if orb = "abort" then
        if HasIsTame(G) and not IsTame(G) then
          SetSize(G,infinity);
          return fail; 
        else return "computation aborted"; fi;
      fi;
      Info(InfoRCWA,3,"RespectedPartition: found orbit of length ",
                      Length(orb),", with representative ",orb[1]);
      P := Union(P,orb);
      if Length(P) > 3 * b and not nc then
        CheckForWildness(G,500,Lcm(List(gens,Mod))^4);
        if HasIsTame(G) and not IsTame(G) then
          SetSize(G,infinity);
          return fail; 
        fi;
      fi;
      density := Sum(List(P,cl->1/cl[2]));
    until density >= 1;
    if density <> 1 then
      # Error("RespectedPartition: internal failure!\n",
      #       "Enter 'return;' to try a different method.\n");
      Info(InfoRCWA,1,"RespectedPartition: advanced method failed. ",
                      " -- Trying standard method ... "); 
      TryNextMethod();
    fi;
    Sort(P,function(c1,c2)
             return c1{[2,1]} < c2{[2,1]};
           end);
    SetModulusOfRcwaMonoid(G,Lcm(List(P,cl->cl[2])));
    return List(P,ResidueClass);
  end );

#############################################################################
##
#M  RespectsPartition( <G>, <P> ) . . . . . . . . . . . . . . for rcwa groups
##
InstallMethod( RespectsPartition,
               "for rcwa groups (RCWA)",
               ReturnTrue, [ IsRcwaGroup, IsList ], 0,

  function ( G, P )
    return ForAll(GeneratorsOfGroup(G),g->RespectsPartition(g,P));
  end );

#############################################################################
##
#S  Tame rcwa groups: ///////////////////////////////////////////////////////
#S  Action and kernel of action on respected partitions. ////////////////////
##
#############################################################################

#############################################################################
##
#M  ActionOnRespectedPartition( <G> ) . . . . . . . . .  for tame rcwa groups
##
InstallMethod( ActionOnRespectedPartition,
               "for tame rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )

    local  P;

    P := RespectedPartition(G);
    return Group(List(GeneratorsOfGroup(G),
                      g->PermutationOpNC(g,P,OnPoints)));
  end );

#############################################################################
##
#M  RankOfKernelOfActionOnRespectedPartition( <G> ) . .  for tame rcwa groups
##
InstallMethod( RankOfKernelOfActionOnRespectedPartition,
               "for tame rcwa groups (RCWA)", true,
               [ IsRcwaGroup ], 0,

  function ( G )

    local  P, H, Pq, Hq, indices, bound, primepowers;

    if IsTrivial(G) then return 0; fi;

    P     := RespectedPartition(G);
    H     := ActionOnRespectedPartition(G);
    bound := Maximum(List(GeneratorsOfGroup(G),
                          g->Maximum(List(Coefficients(g),
                                          c->AbsInt(c[2])))));
    if bound < 7 then bound := 7; fi;
    primepowers := Filtered([2..bound],IsPrimePowerInt);
    Pq := List(primepowers,q->Flat(List(P,cl->SplittedClass(cl,q))));
    Hq := List(Pq,P->Group(List(GeneratorsOfGroup(G),
                                gen->PermutationOpNC(gen,P,OnPoints))));
    indices := List(Hq,Size)/Size(H);
    SetKernelActionIndices(G,indices);
    SetRefinedRespectedPartitions(G,Pq);
    return Maximum(List(Filtered([2..Length(primepowers)],
                                 i->IsPrime(primepowers[i])),
                        j->Number(Factors(indices[j]),p->p=primepowers[j])));
  end );

#############################################################################
##
#M  KernelOfActionOnRespectedPartition( <G> ) . . for tame rcwa groups over Z
##  
InstallMethod( KernelOfActionOnRespectedPartition,
               "for tame rcwa groups over Z (RCWA)", true,
               [ IsRcwaGroupOverZ ], 0,

  function ( G )

    local  P, KFullPoly, KPoly, genKFP, genKPoly, rank, K, H, g, h,
           k, kPoly, genG, genH, genK, cgspar, nrgens, crcs, i;

    P         := RespectedPartition(G);
    H         := ActionOnRespectedPartition(G);
    rank      := RankOfKernelOfActionOnRespectedPartition(G);
    genG      := GeneratorsOfGroup(G);
    genH      := GeneratorsOfGroup(H);
    genK      := [];
    KFullPoly := DirectProduct(List([1..Length(P)],i->DihedralPcpGroup(0)));
    genKFP    := GeneratorsOfGroup(KFullPoly);
    KPoly     := TrivialSubgroup(KFullPoly);
    K         := TrivialSubgroup(G);
    g         := genG[1];
    h         := genH[1];
    nrgens    := Length(genG);
    if Set(KernelActionIndices(G)) <> [1] then
      repeat
        k := g^Order(h);
        if not IsOne(k) then
          kPoly := One(KFullPoly);
          for i in [1..Length(P)] do
            for crcs in Factorization(RestrictedPerm(k,P[i])) do
              if IsClassReflection(crcs) then
                kPoly := kPoly*genKFP[2*i-1];
              elif not IsOne(crcs) then
                kPoly := kPoly*genKFP[2*i]^Determinant(crcs);
              fi;
            od;
          od;
          if not kPoly in KPoly then
            genKPoly := GeneratorsOfGroup(KPoly);
            genK     := GeneratorsOfGroup(K);
            if IsEmpty(genKPoly) then
              genKPoly := [kPoly];
              genK     := [k];
            else
              cgspar   := CgsParallel(genKPoly,genK);
              genKPoly := Concatenation(cgspar[1],[kPoly]);
              genK     := Concatenation(cgspar[2],[k]);
            fi;
            KPoly    := Subgroup(KFullPoly,genKPoly);
            K        := SubgroupNC(G,genK);
            if   ForAll([1..Length(RefinedRespectedPartitions(G))],
                        i->Size(Group(List(GeneratorsOfGroup(K),
                   gen->PermutationOpNC(gen,RefinedRespectedPartitions(G)[i],
                                            OnPoints))))
                 = KernelActionIndices(G)[i])
            then break; fi;
          fi;
        fi;
        i := Random([1..nrgens]);
        g := g * genG[i];
        h := h * genH[i];
      until false;
    fi;
    if not IsBound(genKPoly) then
      genKPoly := [One(KFullPoly)];
      genK     := [One(G)];
    else
      genKPoly := GeneratorsOfGroup(KPoly);
      genK     := GeneratorsOfGroup(K);
      cgspar   := CgsParallel(genKPoly,genK);
      genKPoly := cgspar[1];
      genK     := cgspar[2];
    fi;
    KPoly := Subgroup(KFullPoly,genKPoly);
    K     := SubgroupNC(G,genK);
    SetIndexInParent(K,Size(H));
    SetRespectedPartition(K,P);
    SetRankOfKernelOfActionOnRespectedPartition(K,rank);
    SetKernelOfActionOnRespectedPartition(K,K);
    if   not IsTrivial(K)
    then SetIsomorphismPcpGroup(K,GroupHomomorphismByImagesNC(K,KPoly));
    else SetIsomorphismPcpGroup(K,GroupHomomorphismByImages(K,KPoly,
                                  [One(K)],[One(KPoly)])); fi;
    return K;
  end );

#############################################################################
##
#S  Tame rcwa groups: ///////////////////////////////////////////////////////
#S  Integral conjugates. ////////////////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  IntegralizingConjugator( <G> ) . . . . . . .  for tame rcwa groups over Z
##
InstallMethod( IntegralizingConjugator,
               "for tame rcwa groups over Z (RCWA)", true,
               [ IsRcwaGroupOverZ ], 0,

  function ( G )

    local  P;

    if IsIntegral(G) then return One(G); fi;
    P := RespectedPartition(G); if P = fail then return fail; fi;
    return RepresentativeAction(RCWA(Integers),P,
                                AllResidueClassesModulo(Integers,Length(P)));
  end );

#############################################################################
##
#M  IntegralConjugate( <G> ) . . . . . . . . . . . . . . for tame rcwa groups
##
InstallMethod( IntegralConjugate,
               "for tame rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )

    local  result, R, m;

    result := G^IntegralizingConjugator(G);
    R      := Source(One(result));
    m      := Lcm(List(GeneratorsOfGroup(result),Modulus));
    SetRespectedPartition(result,AllResidueClassesModulo(R,m));
    return result;
  end );

#############################################################################
##
#M  IntegralizingConjugator( <sigma> ) . . . . .  for tame bij. rcwa mappings
##
InstallMethod( IntegralizingConjugator,
               "for tame bijective rcwa mappings (RCWA)", true,
               [ IsRcwaMapping ], 0,
               sigma -> IntegralizingConjugator( Group( sigma ) ) );

#############################################################################
##
#M  IntegralConjugate( <sigma> ) . . . . . . . .  for tame bij. rcwa mappings
##
InstallMethod( IntegralConjugate,
               "for tame bijective rcwa mappings (RCWA)", true,
               [ IsRcwaMapping ], 0,

  function ( sigma )

    local  result, R, m;

    result := sigma^IntegralizingConjugator(sigma);
    R      := Source(result);
    m      := Modulus(result);
    SetRespectedPartition(result,AllResidueClassesModulo(R,m));
    return result;
  end );

#############################################################################
##
#S  Tame rcwa groups: ///////////////////////////////////////////////////////
#S  "Canonical" conjugates of tame rcwa permutations. ///////////////////////
##
#############################################################################

#############################################################################
##
#M  StandardizingConjugator( <sigma> )  for tame bijective rcwa mappings of Z
##
InstallMethod( StandardizingConjugator,
               "for tame bijective rcwa mappings of Z (RCWA)", true,
               [ IsRcwaMappingOfZ ], 0,

  function ( sigma )

    local  toint, int, m, mtilde, mTilde, P, r, rtilde, c, cycs, lngs,
           cohorts, cohort, l, nrcycs, res, cyc, n, ntilde, i, j, k;

    if   not IsBijective(sigma) or not IsClassWiseOrderPreserving(sigma)
      or not IsTame(sigma) or Order(sigma) = infinity
    then TryNextMethod(); fi;
    toint   := IntegralizingConjugator(sigma);
    int     := IntegralConjugate(sigma);
    m       := Modulus(int);
    P       := AllResidueClassesModulo(Integers,m);
    cycs    := Cycles(int,P);
    lngs    := Set(List(cycs,Length));
    cohorts := List(lngs,l->Filtered(cycs,cyc->Length(cyc)=l));
    mtilde  := Sum(lngs);
    c       := List([1..m],i->[1,0,1]);
    rtilde  := 0;
    for cohort in cohorts do
      nrcycs := Length(cohort);
      l      := Length(cohort[1]);
      res    := List([1..l],i->List([1..nrcycs],
                                    j->Residues(cohort[j][i])[1]));
      cyc    := List([1..nrcycs],j->Cycle(int,res[1][j]));
      mTilde := nrcycs * mtilde;
      for i in [1..l] do
        for r in res[i] do
          j      := Position(res[i],r);
          n      := cyc[j][i];
          ntilde := (j-1)*mtilde+rtilde;
          k      := (ntilde*m-mTilde*n-m*rtilde+mtilde*r)/(m*mtilde);
          c[r+1] := [mTilde,k*m*mtilde+m*rtilde-mtilde*r,m];
        od;
        rtilde := rtilde + 1;
      od;
    od; 
    return toint * RcwaMapping(c);
  end );

#############################################################################
##
#M  StandardConjugate( <f> ) . . . . . . . . . . . . . . .  for rcwa mappings
##
InstallMethod( StandardConjugate,
               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
               f -> f^StandardizingConjugator( f ) );

#############################################################################
##
#S  Tame rcwa groups: ///////////////////////////////////////////////////////
#S  Permutation- and matrix representations. ////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  IsomorphismPermGroup( <G> ) . . . . . . . . for finite rcwa groups over Z
##
##  This method makes use of the fact that the class reflection on a residue
##  class r(m) interchanges the residue classes r+m(3m) and r+2m(3m).
##
InstallMethod( IsomorphismPermGroup,
               "for finite rcwa groups (RCWA)",
               true, [ IsRcwaGroupOverZ ], SUM_FLAGS,

  function ( G )

    local  P, P3, H, phi;

    if not IsFinite(G) then
      Error("<G> is infinite");
    fi;
    P   := RespectedPartition(G);
    if   IsClassWiseOrderPreserving(G)
    then H := ActionOnRespectedPartition(G);
    else H := Action(G,Flat(List(P,cl->SplittedClass(cl,3)))); fi;
    phi := Immutable(GroupHomomorphismByImagesNC(G,H));
    SetIsInjective( phi, true );
    if   not HasParent(G)
    then SetNiceMonomorphism(G,phi); SetNiceObject(G,H); fi;
    return phi;
  end );

#############################################################################
##
#M  IsomorphismMatrixGroup( <G> ) . . . . . . . . . . .  for tame rcwa groups
##
InstallMethod( IsomorphismMatrixGroup,
               "for tame rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )

    local  R, res, q, x, d, P, H, M, ModG, g, h, m, deg, r, b, c, pos, i, j;

    if not IsTame(G) then TryNextMethod(); fi;
    R := Source(One(G)); ModG := Modulus(G);
    if IsRcwaGroupOverGFqx(G) then
      q := Size(CoefficientsRing(R));
      x := IndeterminatesOfPolynomialRing(R)[1];
      res := [];
      for d in [1..DegreeOfLaurentPolynomial(ModG)] do
        res[d] := AllGFqPolynomialsModDegree(q,d,x);
      od;
    fi;
    g := GeneratorsOfGroup(G);
    P := RespectedPartition(G);
    deg := 2 * Length(P);
    H := Action(G,P); h := GeneratorsOfGroup(H);
    m := [];
    for i in [1..Length(g)] do
      m[i] := NullMat(deg,deg,R);
      for j in [1..deg/2] do
        b := [[0,0],[0,1]] * One(R);
        r := Residues(P[j])[1] mod Modulus(g[i]);
        if IsRcwaGroupOverZOrZ_pi(G) then
          pos := r+1;
        else
          pos := Position(res[DegreeOfLaurentPolynomial(Mod(g[i]))],r);
        fi;
        c := Coefficients(g[i])[pos];
        b[1] := [c[1]/c[3],c[2]/c[3]];
        m[i]{[2*j^h[i]-1..2*j^h[i]]}{[2*j-1..2*j]} := b;
      od;
    od;
    M := Group(m);
    return GroupHomomorphismByImagesNC(G,M,g,m);
  end );

#############################################################################
##
#M  NiceMonomorphism( <G> ) . . . . . . . . . . . . . .  for tame rcwa groups
##
InstallMethod( NiceMonomorphism,
               "for tame rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )
    if   IsTame(G)
    then if   IsTrivial(KernelOfActionOnRespectedPartition(G))
         then return IsomorphismPermGroup(G);
         else return IsomorphismMatrixGroup(G); fi;
    else TryNextMethod(); fi;
  end );

#############################################################################
##
#M  NiceObject( <G> ) . . . . . . . . . . . . . . . . . . . . for rcwa groups
##
InstallMethod( NiceObject,
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,
               G -> Image( NiceMonomorphism( G ) ) );

#############################################################################
##
#S  Computing the order of an rcwa group. ///////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  Size( <G> ) . . . . . . . . . . . . . . . . . . .  for rcwa groups over Z
##
##  This method checks whether <G> is tame and the kernel of the action of
##  <G> on a respected partition has rank 0 -- if so, it returns the
##  size of the permutation group induced on this partition, and if not it
##  returns infinity.
##
InstallMethod( Size,
               Concatenation("for rcwa groups over Z, use action on ",
                             "respected partition (RCWA)"),
               true, [ IsRcwaGroupOverZ ], 0,

  function ( G )

    local  orbs, orbs_old, gens, g, maxpts;

    # A few `trivial' checks.

    if IsTrivial(G) then return 1; fi;
    if ForAny(GeneratorsOfGroup(G),g->Order(g)=infinity)
    then return infinity; fi;
    if not IsTame(G) then return infinity; fi;

    # On the one hand, an orbit of a finite tame group <G> can intersect
    # in at most 1 resp. 2 points with any residue class in a respected
    # partition, depending on whether <G> is class-wise order-preserving
    # or not. On the other if <G> is infinite, one of its orbits contains
    # an entire residue class from the respected partition.

    orbs := List(RespectedPartition(G),cl->[Representative(cl)]);
    if IsClassWiseOrderPreserving(G) then maxpts := 1; else
      orbs   := Concatenation(orbs,orbs+List(RespectedPartition(G),
                                             cl->[Modulus(cl)]));
      maxpts := 2;
    fi;
    gens := GeneratorsAndInverses(G);
    repeat
      orbs_old := List(orbs,ShallowCopy);
      for g in gens do orbs := List(orbs,orb->Union(orb,orb^g)); od;
    until orbs = orbs_old
       or ForAny(orbs,orb->ForAny(RespectedPartition(G),
                                  cl->Length(Intersection(cl,orb))>maxpts));

    if orbs = orbs_old then return Size(Action(G,Union(orbs)));
                       else return infinity; fi;
  end );

#############################################################################
##
#M  Size( <G> ) . . . . . . . . . . . . . . . . for rcwa groups over GF(q)[x]
##
InstallMethod( Size,
               "for rcwa groups over GF(q)[x] (RCWA)",
               true, [ IsRcwaGroupOverGFqx ], 0,

  function ( G )

    local  R, p, B, P, Pp, H, size, r;

    R := Source(One(G));
    p := Characteristic(R);

    for r in [1..3] do
      B := Ball(G,One(G),r:Spheres)[r+1];
      if ForAny(B,g->Loops(g)<>[]) then
        SetIsTame(G,false);
        return infinity;
      fi;
    od;
  
    P := RespectedPartition(G);
    if P = fail then
      SetIsTame(G,false);
      return infinity;
    else
      SetIsTame(G,true);
      Pp   := Flat(List(P,cl->SplittedClass(cl,p)));
      H    := Action(G,Pp);
      size := Size(H);
      return size;
    fi;

  end );

#############################################################################
##
#M  Size( <G> ) . . . . . . . . . . . . . . . . . . . . . . . for rcwa groups
##
##  This method tests for tameness and looks for elements of infinite order.
##
InstallMethod( Size,
               "for rcwa groups, test for tameness (RCWA)",
               true, [ IsRcwaGroup ], 0,

  function ( G )

    local  gen, k;

    Info(InfoRCWA,1,
         "Size: Test for tameness and look for elements of infinite order.");
    if not IsTame(G) then return infinity; fi;
    if   IsTame(G) and Characteristic(Source(One(G))) <> 0
    then TryNextMethod(); fi; # In this case, <G> is finite.
    gen := GeneratorsOfGroup(G);
    if ForAny(gen,g->Order(g)=infinity) then return infinity; fi;
    if   ForAny(Combinations(gen,2),t->Order(Comm(t[1],t[2]))=infinity)
    then return infinity; fi;
    for k in [2..3] do
      if   ForAny(Tuples(gen,k),t->Order(Product(t))=infinity)
      then return infinity; fi;
    od;
    TryNextMethod();
  end );

#############################################################################
##
#S  The membership- / subgroup test. ////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  \in( <g>, <G> ) . . . . . . for rcwa groups with membership test function
##
InstallMethod( \in,
               "for rcwa groups with membership test function (RCWA)",
               ReturnTrue,
               [ IsRcwaMapping, IsRcwaGroup and HasElementTestFunction ], 0,

  function ( g, G )
    if HasParent(G) and not g in Parent(G) then return false; fi;
    return ElementTestFunction(G)(g);
  end );

#############################################################################
##
#M  \in( <g>, <G> ) . . . . . . . . . . . . . . . . . . . . . for rcwa groups
##
##  This method may run into an infinite loop if <G> is infinite and <g> is
##  not an element of <G>.
##
InstallMethod( \in,
               "for rcwa groups (RCWA)", ReturnTrue,
               [ IsRcwaMapping, IsRcwaGroup and HasGeneratorsOfGroup ], 0,

  function ( g, G )

    local  gens, phi;

    if FamilyObj(g) <> FamilyObj(One(G)) then return false; fi;
    gens := GeneratorsOfGroup(G);
    if IsOne(g) or g in gens or g^-1 in gens then return true; fi;
    if not IsSubset(PrimeSet(G),PrimeSet(g)) then return false; fi;
    if   g in List(Combinations(gens,2), t -> Product(t))
    then return true; fi;
    Info(InfoRCWA,2,"\\in: trying to factor <g> into gen's ...");
    phi := EpimorphismFromFreeGroup(G);
    return PreImagesRepresentativeNC(phi,g) <> fail;
  end );

#############################################################################
##
#M  \in( <g>, <G> ) . . . . . . . . . . . . . . . . .  for rcwa groups over Z
##
##  If <G> is wild this method may run into an infinite loop if <g> is not an
##  element of <G>.
##
InstallMethod( \in,
               "for rcwa groups over Z (RCWA)", ReturnTrue,
               [ IsRcwaMappingOfZ,
                 IsRcwaGroupOverZ and HasGeneratorsOfGroup ], 0,

  function ( g, G )

    local  P, H, h, K, k, KPoly, KFullPoly, genKFP, kPoly, crcs,
           F, phi, gens, orbs, orbsmod, m, max, i;

    Info(InfoRCWA,2,"\\in for an rcwa permutation <g> of Z ",
                    "and an rcwa group <G> over Z");
    if not IsBijective(g)
    then Info(InfoRCWA,4,"<g> is not bijective."); return false; fi;
    gens := GeneratorsOfGroup(G);
    if IsOne(g) or g in gens or g^-1 in gens then
      Info(InfoRCWA,2,"<g> = 1 or one of <g> or <g>^-1 ",
                      "in generator list of <G>.");
      return true;
    fi;
    if not IsSubset(PrimeSet(G),PrimeSet(g)) then
      Info(InfoRCWA,2,"<g> and <G> have incompatible prime sets.");
      return false;
    fi;
    if not IsSubset(Factors(   Product(List(gens,Multiplier))
                             * Product(List(gens,Divisor))),
                    Filtered(Factors(Multiplier(g)*Divisor(g)),p->p<>1))
    then
      Info(InfoRCWA,2,"The multiplier or divisor of <g> has factors which");
      Info(InfoRCWA,2,"no multiplier or divisor of a generator of <G> has.");
      return false;
    fi;
    if        IsClassWiseOrderPreserving(G)
      and not IsClassWiseOrderPreserving(g) then
      Info(InfoRCWA,2,"<G> is class-wise order-preserving, but <g> is not.");
      return false;
    fi;
    if Sign(g) = -1 and Set(gens,Sign) = [1] then
      Info(InfoRCWA,2,"Sign(<g>) is -1, but the sign of all generators ",
                      "of <G> is 1.");
      return false;
    fi;
    if Determinant(g) <> 0 and IsClassWiseOrderPreserving(G)
      and Set(gens,Determinant) = [0]
    then
      Info(InfoRCWA,2,"<G> lies in the kernel of the determinant ",
                      "epimorphism, but <g> does not.");
      return false;
    fi;
    if not IsSubset(Support(G),Support(g)) then
      Info(InfoRCWA,2,"Support(<g>) is not a subset of Support(<G>).");
      return false;
    fi;
    if IsSignPreserving(G) and not IsSignPreserving(g) then
      Info(InfoRCWA,2,"<G> fixes the nonnegative integers setwise,");
      Info(InfoRCWA,2,"but <g> does not.");
      return false;
    fi;
    if not IsTame(G) then
      orbs := ShortOrbits(G,Intersection(Support(G),[-100..100]),50);
      if orbs <> [] then
        if ForAny(orbs,orb->Permutation(g,orb)=fail) then
          Info(InfoRCWA,2,"<g> does not act on some finite orbit of <G>.");
          return false;
        fi;
        if ForAny(orbs,orb->not Permutation(g,orb) in Action(G,orb)) then
          Info(InfoRCWA,2,"<g> induces a permutation on some finite orbit");
          Info(InfoRCWA,2,"of <G> which does not lie in the group induced");
          Info(InfoRCWA,2,"by <G> on this orbit.");
          return false;
        fi;
      fi;
      m       := Lcm(List(Concatenation(gens,[g]),Modulus));
      orbsmod := List(ProjectionsToInvariantUnionsOfResidueClasses(G,m),
                      proj->Support(Image(proj)));
      if ForAny(orbsmod,orb->orb^g<>orb) then
        Info(InfoRCWA,2,"<g> does not leave the partition of Z into unions");
        Info(InfoRCWA,2,"of residue classes (mod ",m,") invariant which is");
        Info(InfoRCWA,2,"fixed by <G>.");
        return false;
      fi;
      Info(InfoRCWA,2,"<G> is wild -- trying to factor <g> into gen's ...");
      phi := EpimorphismFromFreeGroup(G);
      return PreImagesRepresentativeNC(phi,g) <> fail;
    else
      if Modulus(G) mod Modulus(g) <> 0 then
        Info(InfoRCWA,2,"Mod(<g>) does not divide Mod(<G>).");
        return false;
      fi;
      if not IsTame(g) then
        Info(InfoRCWA,2,"<G> is tame, but <g> is wild.");
        return false;
      fi;
      if IsFinite(G) and Order(g) = infinity then
        Info(InfoRCWA,2,"<G> is finite, but <g> has infinite order.");
        return false;
      fi;
      P := RespectedPartition(G);
      H := ActionOnRespectedPartition(G);
      h := Permutation(g,P);
      if h = fail then
        Info(InfoRCWA,2,"<g> does not act on RespectedPartition(<G>).");
        return false;
      fi;
      if not RespectsPartition(g,P) then
        Info(InfoRCWA,2,"<g> does not respect RespectedPartition(<G>).");
        return false;
      fi;
      if not h in H then
        Info(InfoRCWA,2,"The permutation induced by <g> on ",
                        "P := RespectedPartition(<G>)");
        Info(InfoRCWA,2,"is not an element of the permutation group ",
                        "which is induced by <G> on P.");
        return false;
      fi;

      Info(InfoRCWA,2,"Computing an element of <G> which acts like <g>");
      Info(InfoRCWA,2,"on RespectedPartition(<G>).");

      phi := EpimorphismFromFreeGroup(H);
      h   := PreImagesRepresentativeNC(phi,h:NoStabChain);
      if h = fail then return false; fi;
      h   := Product(List(LetterRepAssocWord(h),
                          id->gens[AbsInt(id)]^SignInt(id)));
      k   := g/h;

      Info(InfoRCWA,2,"Checking membership of the quotient in the kernel ");
      Info(InfoRCWA,2,"of the action of <g> on RespectedPartition(<G>).");

      K         := KernelOfActionOnRespectedPartition(G);
      KPoly     := Range(IsomorphismPcpGroup(K));
      KFullPoly := Parent(KPoly);
      genKFP    := GeneratorsOfGroup(KFullPoly);
      kPoly     := One(KFullPoly);
      for i in [1..Length(P)] do
        for crcs in Factorization(RestrictedPerm(k,P[i])) do
          if IsClassReflection(crcs) then
            kPoly := kPoly*genKFP[2*i-1];
          elif not IsOne(crcs) then
            kPoly := kPoly*genKFP[2*i]^Determinant(crcs);
          fi;
        od;
      od;
      return kPoly in KPoly;
    fi;
  end );

#############################################################################
##
#M  \in( <g>, <G> ) . . . . . . . . . . . . . . for rcwa groups over GF(q)[x]
##
##  If <G> is wild this method may run into an infinite loop if <g> is not an
##  element of <G>.
##
InstallMethod( \in,
               "for rcwa groups over GF(q)[x] (RCWA)", ReturnTrue,
               [ IsRcwaMappingOfGFqx, IsRcwaGroupOverGFqx ], 0,

  function ( g, G )

    local  R, x, P, H, h, phi, gens, orbs, orbsmod, m;

    R := Source(g); x := IndeterminatesOfPolynomialRing(R)[1];
    Info(InfoRCWA,2,"\\in for an rcwa permutation <g> of ",RingToString(R));
    Info(InfoRCWA,2,"    and an rcwa group <G> over ",RingToString(R));
    if   not IsBijective(g)
    then Info(InfoRCWA,4,"<g> is not bijective."); return false; fi;
    gens := GeneratorsOfGroup(G);
    if IsOne(g) or g in gens or g^-1 in gens then
      Info(InfoRCWA,2,"<g> = 1 or one of <g> or <g>^-1 ",
                      "in generator list of <G>.");
      return true;
    fi;
    if not IsSubset(PrimeSet(G),PrimeSet(g)) then
      Info(InfoRCWA,2,"<g> and <G> have incompatible prime sets.");
      return false;
    fi;
    if not IsSubset(Factors(   Product(List(gens,Multiplier))
                             * Product(List(gens,Divisor))),
                    Filtered(Factors(Multiplier(g)*Divisor(g)),
                             p->not IsOne(p)))
    then
      Info(InfoRCWA,2,"The multiplier or divisor of <g> has factors which");
      Info(InfoRCWA,2,"no multiplier or divisor of a generator of <G> has.");
      return false;
    fi;
    if not IsSubset(Support(G),Support(g)) then
      Info(InfoRCWA,2,"Support(<g>) is not a subset of Support(<G>).");
      return false;
    fi;
    if not IsTame(G) then
      orbs := ShortOrbits(G,Intersection(Support(G),AllResidues(R,x^4)),30);
      if orbs <> [] then
        if ForAny(orbs,orb->Permutation(g,orb)=fail) then
          Info(InfoRCWA,2,"<g> does not act on some finite orbit of <G>.");
          return false;
        fi;
        if ForAny(orbs,orb->not Permutation(g,orb) in Action(G,orb)) then
          Info(InfoRCWA,2,"<g> induces a permutation on some finite orbit");
          Info(InfoRCWA,2,"of <G> which does not lie in the group induced");
          Info(InfoRCWA,2,"by <G> on this orbit.");
          return false;
        fi;
      fi;
      m       := Lcm(List(Concatenation(gens,[g]),Modulus));
      orbsmod := List(ProjectionsToInvariantUnionsOfResidueClasses(G,m),
                      proj->Support(Image(proj)));
      if ForAny(orbsmod,orb->orb^g<>orb) then
        Info(InfoRCWA,2,"<g> does not leave the partition of ",
                        RingToString(R)," into");
        Info(InfoRCWA,2,"unions of residue classes (mod ",m,") invariant");
        Info(InfoRCWA,2,"which is fixed by <G>.");
        return false;
      fi;
      Info(InfoRCWA,2,"<G> is wild -- trying to factor <g> into gen's ...");
      phi := EpimorphismFromFreeGroup(G);
      return PreImagesRepresentativeNC(phi,g) <> fail;
    else
      if Modulus(G) mod Modulus(g) <> 0 then
        Info(InfoRCWA,2,"Mod(<g>) does not divide Mod(<G>).");
        return false;
      fi;
      if not IsTame(g) then # This covers also the case of infinite order.
        Info(InfoRCWA,2,"<G> is tame, but <g> is wild.");
        return false;
      fi;
      P := RespectedPartition(G);
      H := ActionOnRespectedPartition(G);
      h := Permutation(g,P);
      if h = fail then
        Info(InfoRCWA,2,"<g> does not act on RespectedPartition(<G>).");
        return false;
      fi;
      if not RespectsPartition(g,P) then
        Info(InfoRCWA,2,"<g> does not respect RespectedPartition(<G>).");
        return false;
      fi;
      if not h in H then
        Info(InfoRCWA,2,"The permutation induced by <g> on ",
                        "P := RespectedPartition(<G>)");
        Info(InfoRCWA,2,"is not an element of the permutation group ",
                        "which is induced by <G> on P.");
        return false;
      elif CoefficientsRing(R) = GF(2) # R has no units except of 1.
      then return true; fi;
      Info(InfoRCWA,2,"Trying to factor <g> into gen's ...");
      phi := EpimorphismFromFreeGroup(G);            # <G> is tame -> this
      return PreImagesRepresentativeNC(phi,g) <> fail; # could be improved.
    fi;
  end );

#############################################################################
##
#M  IsSubset( <G>, <H> ) . . . . . . . . . . . . . . . .  for two rcwa groups
##
##  This method checks for a subgroup relation.
##
InstallMethod( IsSubset,
               "for two rcwa groups (RCWA)", true,
               [ IsRcwaGroup and HasGeneratorsOfGroup,
                 IsRcwaGroup and HasGeneratorsOfGroup ], 0,

  function ( G, H )

    if   IsSubset(GeneratorsOfGroup(G),GeneratorsOfGroup(H))
    then return true; fi;
    if not IsSubset(PrimeSet(G),PrimeSet(H)) then return false; fi;
    if not IsSubset(Support(G),Support(H)) then return false; fi;
    if IsTame(G) and not IsTame(H) then return false; fi;
    if IsTame(G) and IsTame(H) then
      if not IsZero(Multiplier(G) mod Multiplier(H)) then return false; fi;
      if   not RespectsPartition(H,RespectedPartition(G))
      then return false; fi;
      if not IsSubgroup(ActionOnRespectedPartition(G),
                        Action(H,RespectedPartition(G)))
      then return false; fi;
      if Size(H) > Size(G) then return false; fi;
    fi;
    return ForAll(GeneratorsOfGroup(H),h->h in G);
  end );

#############################################################################
##
#M  IsSubset( <G>, <H> ) . . . . . . . . . . . . . for two rcwa groups over Z
##
##  This method checks for a subgroup relation.
##
InstallMethod( IsSubset,
               "for two rcwa groups over Z (RCWA)", true,
               [ IsRcwaGroupOverZ and HasGeneratorsOfGroup,
                 IsRcwaGroupOverZ and HasGeneratorsOfGroup ], 0,

  function ( G, H )

    local  gensG, gensH, dG, dH;

    gensG := GeneratorsOfGroup(G); gensH := GeneratorsOfGroup(H);
    if IsSubset(gensG,gensH) then return true; fi;
    if not IsSubset(PrimeSet(G),PrimeSet(H)) then return false; fi;
    if not IsSubset(Support(G),Support(H)) then return false; fi;
    if   ForAll(gensG,g->Sign(g) = 1) and ForAny(gensH,h->Sign(h)=-1)
    then return false; fi;
    if IsClassWiseOrderPreserving(G) then
      if not IsClassWiseOrderPreserving(H) then return false; fi;
      dG := Gcd(List(gensG,Determinant));
      dH := Gcd(List(gensH,Determinant));
      if   dG = 0 and dH <> 0 or dG <> 0 and dH mod dG <> 0
      then return false; fi;
    fi;
    if IsTame(G) and not IsTame(H) then return false; fi;
    if IsTame(G) and IsTame(H) then
      if Multiplier(G) mod Multiplier(H) <> 0 then return false; fi;
      if   not RespectsPartition(H,RespectedPartition(G))
      then return false; fi;
      if not IsSubgroup(ActionOnRespectedPartition(G),
                        Action(H,RespectedPartition(G)))
      then return false; fi;
      if   Size(H) > Size(G)
        or RankOfKernelOfActionOnRespectedPartition(H)
         > RankOfKernelOfActionOnRespectedPartition(G)
      then return false; fi;
    fi;
    return ForAll(gensH,h->h in G);
  end );

#############################################################################
##
#M  \=( <G>, <H> ) . . . . . . . . . . . . . . . . . . .  for two rcwa groups
##
InstallMethod( \=,
               "for two rcwa groups (RCWA)", true,
               [ IsRcwaGroup and HasGeneratorsOfGroup,
                 IsRcwaGroup and HasGeneratorsOfGroup ], 0,

  function ( G, H )

    local  gensG, gensH;

    gensG := Set(GeneratorsOfGroup(G)); gensH := Set(GeneratorsOfGroup(H));
    if gensG = gensH then return true; fi;
    if PrimeSet(G) <> PrimeSet(H) then return false; fi;
    if Support(G) <> Support(H) then return false; fi;
    if IsTame(G) <> IsTame(H) then return false; fi;
    if IsTame(G) and IsTame(H) then
      if Multiplier(G) <> Multiplier(H) then return false; fi;
      if   RespectedPartition(G) <> RespectedPartition(H)
      then return false; fi;
      if   ActionOnRespectedPartition(G) <> ActionOnRespectedPartition(H)
      then return false; fi;
      if Size(G) <> Size(H) then return false; fi;
    fi;
    return ForAll(gensH,h->h in G) and ForAll(gensG,g->g in H);
  end );

#############################################################################
##
#M  \=( <G>, <H> ) . . . . . . . . . . . . . . . . for two rcwa groups over Z
##
InstallMethod( \=,
               "for two rcwa groups over Z (RCWA)", true,
               [ IsRcwaGroupOverZ and HasGeneratorsOfGroup,
                 IsRcwaGroupOverZ and HasGeneratorsOfGroup ], 0,

  function ( G, H )

    local  gensG, gensH, dG, dH;

    gensG := Set(GeneratorsOfGroup(G)); gensH := Set(GeneratorsOfGroup(H));
    if gensG = gensH then return true; fi;
    if PrimeSet(G) <> PrimeSet(H) then return false; fi;
    if Support(G) <> Support(H) then return false; fi;
    if IsSignPreserving(G) <> IsSignPreserving(H) then return false; fi;
    if   (ForAll(gensG,g->Sign(g) = 1) and ForAny(gensH,h->Sign(h)=-1))
      or (ForAll(gensH,h->Sign(h) = 1) and ForAny(gensG,g->Sign(g)=-1))
    then return false; fi;
    if   IsClassWiseOrderPreserving(G) <> IsClassWiseOrderPreserving(H)
    then return false; fi;
    if   IsClassWiseOrderPreserving(G) then
      dG := Gcd(List(gensG,Determinant));
      dH := Gcd(List(gensH,Determinant));
      if dG <> dH then return false; fi;
    fi;
    if IsTame(G) <> IsTame(H) then return false; fi;
    if IsTame(G) then
      if Multiplier(G) <> Multiplier(H) then return false; fi;
      if   RespectedPartition(G) <> RespectedPartition(H)
      then return false; fi;
      if   ActionOnRespectedPartition(G) <> ActionOnRespectedPartition(H)
      then return false; fi;
      if Size(G) <> Size(H) then return false; fi;
      if    RankOfKernelOfActionOnRespectedPartition(G)
         <> RankOfKernelOfActionOnRespectedPartition(H)
      then return false; fi;
    fi;
    return ForAll(gensH,h->h in G) and ForAll(gensG,g->g in H);
  end );

#############################################################################
##
#S  The action of rcwa groups on subsets of the underlying ring. ////////////
##
#############################################################################

#############################################################################
##
#F  ActionHomomorphism( <G>, <S> )  of an rcwa group on a residue class union
##
BindGlobal( "ActionHomomorphism_LIBRARY", ActionHomomorphism ); #
MakeReadWriteGlobal( "ActionHomomorphism" );                    # dirty hack
Unbind( ActionHomomorphism );                                   #
BindGlobal( "ActionHomomorphism",

  function ( arg )

    local  G, H, S, phi, imgs;

    if   not IsRcwaGroup(arg[1]) or Length(arg) <> 2
      or not IsResidueClassUnion(arg[2])
      or not IsSubset(Source(One(arg[1])),arg[2])
    then return CallFuncList(ActionHomomorphism_LIBRARY,arg); fi;
    G := arg[1]; S := arg[2];
    imgs := List(GeneratorsOfGroup(G),g->RestrictedPerm(g,S));
    H := GroupWithGenerators(imgs);
    phi := GroupHomomorphismByImagesNC(G,H);
    return phi;
  end );

#############################################################################
##
#F  Action( <G>, <S> ) . . . . . .  of an rcwa group on a residue class union
#F  Action( <M>, <S> ) . . . . . . of an rcwa monoid on a residue class union
#F  Action( <M>, <l> ) . . . . . . . . . .  of an rcwa monoid on a finite set
##
BindGlobal( "Action_LIBRARY", Action ); #
MakeReadWriteGlobal( "Action" );        # dirty hack ...
Unbind( Action );                       #
BindGlobal( "Action",

  function ( arg )

    local  G, M, S;

    if   not IsRcwaMonoid(arg[1]) or Length(arg) <> 2
      or not (IsResidueClassUnion(arg[2]) or IsList(arg[2]))
      or not IsSubset(Source(One(arg[1])),arg[2])
    then return CallFuncList(Action_LIBRARY,arg); fi;
    if IsRcwaGroup(arg[1]) then
      G := arg[1]; S := arg[2];
      return Image(ActionHomomorphism(G,S));
    else
      M := arg[1]; S := arg[2];
      if IsResidueClassUnion(S) then
        return MonoidByGenerators(List(GeneratorsOfMonoid(M),
                 f->RestrictedMapping(f,S)));
      else
        return MonoidByGenerators(List(GeneratorsOfMonoid(M),
                 f->Transformation(List(OnTuples(S,f),n->Position(S,n)))));
      fi;
    fi;
  end );

#############################################################################
##
#S  Stabilizers. ////////////////////////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#F  Stabilizer( <arg> ) . . . . . . . . . .  use StabilizerOp for rcwa groups
##
MakeReadWriteGlobal( "Stabilizer" ); Unbind( Stabilizer ); # dirty hack ...
BindGlobal( "Stabilizer",                                  #

  function ( arg )
    if   Length( arg ) = 1   then return StabilizerOfExternalSet( arg[1] );
    elif IsRcwaGroup(arg[1]) then return CallFuncList( StabilizerOp, arg );
    else return CallFuncList( StabilizerFunc, arg ); fi;
    return;
  end );

#############################################################################
##
#M  StabilizerOp( <G>, <point> )  point stabilizer in an rcwa group, symbolic
##
InstallMethod( StabilizerOp,
               "point stabilizer in an rcwa group, symbolic (RCWA)",
               ReturnTrue, [ IsRcwaGroup, IsRingElement ], 5,

  function ( G, point )

    local  S;

    if   IsFinitelyGeneratedGroup(G) and ValueOption("symbolic") <> true
    then TryNextMethod(); fi;
    if not point in Support(G) then return G; fi;

    S := SubgroupByProperty(G,g->point^g=point);
    SetStabilizerInfo(S, rec(set    := [point],
                             action := OnPoints));
    return S;
  end );

#############################################################################
##
#M  StabilizerOp( <G>, <set>, <action> )  set stabilizer in rcwa group, symb.
##
InstallMethod( StabilizerOp,
               "set stabilizer in an rcwa group, symbolic (RCWA)",
               ReturnTrue, [ IsRcwaGroup, IsListOrCollection,
                             IsFunction ], 0,

  function ( G, set, action )

    local  S;

    set := Intersection(set,Support(G));
    if set = [] then return G; fi;

    if not IsFinite(set) and action = OnTuples then
      S := SubgroupByProperty(G,g->Intersection(Support(g),set)=[]);
    else
      S := SubgroupByProperty(G,g->action(set,g)=set);
    fi;

    SetStabilizerInfo(S, rec(set    := set,
                             action := action));
    return S;
  end );

#############################################################################
##
#M  StabilizerOp( <G>, <point> ) . point stabilizer in an rcwa group, default
##
InstallMethod( StabilizerOp,
               "point stabilizer in an rcwa group, default method (RCWA)",
               ReturnTrue, [ IsRcwaGroup, IsRingElement ], 0,

  function ( G, point )

    local  S, gens, orb, t, g, p, img, maxgens;

    if not point in Support(G) then return G; fi;

    if ValueOption("symbolic") = true then TryNextMethod(); fi;
    if   ValueOption("maxr") <> fail or ValueOption("maxm") <> fail
    then TryNextMethod(); fi;
    maxgens := GetOption("maxgens",infinity,IsPosInt);

    gens := GeneratorsOfGroup(G);
    orb  := [point];
    t    := [One(G)];
    S    := TrivialSubgroup(G);
    for p in orb do
      for g in gens do
        img := p^g;
        if not img in orb then
          Add(orb,img);
          Add(t,t[Position(orb,p)]*g);
        else
          S := ClosureGroup(S,t[Position(orb,p)]*g/t[Position(orb,img)]);
          if Length(GeneratorsOfGroup(S)) >= maxgens then return S; fi;
        fi;
      od;
    od;

    return S;
  end );

#############################################################################
##
#M  StabilizerOp( <G>, <point> ) . (subgp. of) point stabilizer in rcwa group
##
InstallMethod( StabilizerOp,
               "(subgroup of) point stabilizer in an rcwa group (RCWA)",
               ReturnTrue, [ IsRcwaGroup, IsRingElement ], 0,

  function ( G, point )

    local  S, B, gens, maxr, maxm;

    if not point in Support(G) then return G; fi;

    maxr := ValueOption("maxr"); maxm := ValueOption("maxm");
    if maxr = fail or maxm = fail then TryNextMethod(); fi;

    B    := RestrictedBall(G,One(G),maxr,maxm:Spheres);
    gens := Union(List(B,sphere->Filtered(sphere,
                                          g->not point in Support(g))));
    S    := Group(gens);

    return S;
  end );

#############################################################################
##
#M  StabilizerOp( <G>, <set> ) (subgp. of) pointwise stabilizer in rcwa group
##
InstallMethod( StabilizerOp,
               "(subgroup of) pointwise stabilizer in an rcwa group (RCWA)",
               ReturnTrue, [ IsRcwaGroup, IsListOrCollection ], 0,

  function ( G, set )

    local  S, gens, B, maxr, maxm;

    set := Intersection(set,Support(G));
    if set = [] then return G; fi;

    maxr := ValueOption("maxr"); maxm := ValueOption("maxm");
    if maxr = fail or maxm = fail then TryNextMethod(); fi;

    B    := RestrictedBall(G,One(G),maxr,maxm:Spheres);
    gens := Union(List(B,sphere->Filtered(sphere,
                                          g->Intersection(Support(g),set)
                                            =[])));
    S    := Group(gens);

    return S;
  end );

#############################################################################
##
#M  IsTrivial( <G> ) . . . . . . . . . . . . . for stabilizers in rcwa groups
##
InstallMethod( IsTrivial,
               "for stabilizers in rcwa groups", true,
               [ IsRcwaGroup and HasParent and
                 HasElementTestFunction and HasStabilizerInfo ], 0,

  function ( G )

    local  iter;

    if     IsFinite(Difference(Support(Parent(G)),StabilizerInfo(G).set))
       and StabilizerInfo(G).action = OnTuples
    then return true; fi;
    if IsNaturalRCWA_OR_CT(Parent(G)) then return false; fi;
    iter := Iterator(G);
    NextIterator(iter);
    return IsDoneIterator(iter);
  end );

#############################################################################
##
#S  Testing for transitivity. ///////////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  IsTransitive( <G>, <S> ) . .  for an rcwa group and a residue class union
##
##  This method might fail or run into an infinite loop.
##
InstallMethod( IsTransitive,
               "for an rcwa group and a residue class union (RCWA)",
               ReturnTrue, [ IsRcwaGroup, IsListOrCollection ], 0,

  function ( G, S )

    local  ShortOrbitsFound, ShiftedClass, ShiftedClassPowers,
           R, H, x, gen, comm, el, WordLng, cl, ranges, range;

    ShortOrbitsFound := function ( range, maxlng )
      
      local  orb;

      orb := ShortOrbits(G,range,maxlng);
      if orb <> [] then
        Info(InfoRCWA,1,"The group has the finite orbits ",orb);
        return true;
      else return false; fi;
    end;

    ShiftedClass := function ( el )

      local  cl, g, c, m, r, res, e;

      e := One(R);
      for g in el do
        c := Coefficients(g); m := Modulus(g);
        res := Filtered( AllResidues(R,m), r ->    c[r+1]{[1,3]} = [e,e]
                                               and not IsZero(c[r+1][2])
                                               and IsZero(c[r+1][2] mod m) );
        if res <> [] then
          r := res[1]; m := StandardAssociate(R,c[r+1][2]);
          cl := ResidueClass(R,m,r);
          if IsSubset(S,cl) then
            Info(InfoRCWA,1,"A cyclic subgroup has been found which acts ",
                 "transitively\n#I  on the residue class ",r,"(",m,").");
            return cl;
          fi;
        fi;
      od;
      return fail;
    end;

    ShiftedClassPowers := function ( el )

      local  g, m, cl, exp, pow, n, e;

      exp := [2,2,3,5,2,7,3,2,11,13,5,3,17,19,2];
      for g in el do
        pow := g; m := Modulus(g); e := 1;
        for n in exp do
          if Length(AllResidues(R,Modulus(pow))) > 4*Length(AllResidues(R,m))
          then break; fi;
          pow := pow^n; e := e * n;
          cl := ShiftedClass([pow]);
          if cl <> fail then return cl; fi;
        od;
      od;
      return fail;
    end;

    R := Source(One(G));

    if not IsSubset(R,S) then TryNextMethod(); fi;
    if not ForAll(GeneratorsOfGroup(G),g->S^g=S) then return false; fi;

    if IsFinite(S) then

      H := Action(G,AsList(S));
      return NrMovedPoints(H) = Size(S) and IsTransitive(H,MovedPoints(H));

    elif IsRcwaGroupOverZ(G) and IsSignPreserving(G) then

      if IsIntegers(S) or IsResidueClassUnion(S) then
        Info(InfoRCWA,1,"IsTransitive: group is sign-preserving, thus is");
        Info(InfoRCWA,1,"not transitive on Z or a union of residue classes");
        return false;
      else
        TryNextMethod();
      fi;

    else

      Info(InfoRCWA,1,"IsTransitive: ",
                      "finiteness test and search for short orbits ...");

      if   HasIsFinite(G) and IsFinite(G)
      then Info(InfoRCWA,1,"The group is finite."); return false; fi;
      if   (IsRcwaGroupOverGFqx(G) or IsZ_pi(R))
        and IsFinitelyGeneratedGroup(G) and HasIsTame(G) and IsTame(G)
      then return false; fi;

      if   IsIntegers(R) or IsZ_pi(R) then
        ranges := [[-10..10],[-30..30],[-100..100]];
      elif IsZxZ(R) then
        ranges := List([[[2,0],[0,2]],[[4,0],[0,4]],
                        [[8,0],[0,8]],[[16,0],[0,16]]],m->AllResidues(R,m));
      elif IsPolynomialRing(R) then
        x := IndeterminatesOfPolynomialRing(R)[1];
        ranges := [AllResidues(R,x^2),AllResidues(R,x^3),
                   AllResidues(R,x^4),AllResidues(R,x^6)];
      fi;
      ranges := List(ranges,range->Intersection(range,S));
      for range in ranges do
        if ShortOrbitsFound(range,4*Length(range)) then return false; fi;
      od;

      if   IsFinite(G)
      then Info(InfoRCWA,1,"The group is finite."); return false; fi;

      if   (IsRcwaGroupOverGFqx(G) or IsZ_pi(R))
        and IsFinitelyGeneratedGroup(G) and IsTame(G)
      then return false; fi;

      if IsIntegers(R) then 

        Info(InfoRCWA,1,"Searching for class shifts ...");
        Info(InfoRCWA,2,"... in generators");
        gen := GeneratorsOfGroup(G); cl := ShiftedClass(gen);
        if cl <> fail then return OrbitUnion(G,cl) = S; fi;
        Info(InfoRCWA,2,"... in commutators of the generators");
        comm := List(Combinations(gen,2),g->Comm(g[1],g[2]));
        cl := ShiftedClass(comm);
        if cl <> fail then return OrbitUnion(G,cl) = S; fi;
        Info(InfoRCWA,2,"... in powers of the generators");
        cl := ShiftedClassPowers(gen);
        if cl <> fail then return OrbitUnion(G,cl) = S; fi;
        Info(InfoRCWA,2,"... in powers of commutators of the generators");
        cl := ShiftedClassPowers(comm);
        if cl <> fail then return OrbitUnion(G,cl) = S; fi;
        gen := Union(gen,List(gen,Inverse)); el := gen; WordLng := 1;
        repeat
          WordLng := WordLng + 1;
          Info(InfoRCWA,2,"... in powers of words of length ",WordLng,
                          " in the generators");
          el := Union(el,Flat(List(el,g->g*gen)));
          cl := ShiftedClassPowers(el);
          if cl <> fail then return OrbitUnion(G,cl) = S; fi;
        until false;

      else

        Info(InfoRCWA,1,"... this method gives up ...");
        TryNextMethod();

      fi;

    fi;

  end );

#############################################################################
##
#M  IsTransitive( <G>, NonnegativeIntegers ) for an rcwa group over Z and N_0
##
##  This method might fail or run into an infinite loop.
##
InstallMethod( IsTransitive,
               "for an rcwa group over Z and N_0 (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsNonnegativeIntegers ], 0,

  function ( G, N_0 )

    if not IsIntegers(Union(Support(G),ExcludedElements(Support(G))))
      or (    ExcludedElements(Support(G)) <> []
          and Maximum(ExcludedElements(Support(G))) >= 0)
    then return false;
    else return IsTransitiveOnNonnegativeIntegersInSupport(G); fi;
  end );

#############################################################################
##
#M  IsTransitiveOnNonnegativeIntegersInSupport( <G> ) . . . .  default method
##
InstallMethod( IsTransitiveOnNonnegativeIntegersInSupport,
               "for an rcwa group over Z (RCWA)", true,
               [ IsRcwaGroupOverZ ], 0,

  function ( G )

    local  info;

    info := TryIsTransitiveOnNonnegativeIntegersInSupport(G,7);
    Info(InfoRCWA,1,"`IsTransitiveOnNonnegativeIntegersInSupport': ",info);
    if HasIsTransitiveOnNonnegativeIntegersInSupport(G) then
      return IsTransitiveOnNonnegativeIntegersInSupport(G);
    else
     Info(InfoRCWA,1,"cannot decide transitivity, you might try");
     Info(InfoRCWA,1,"TryIsTransitiveOnNonnegativeIntegersInSupport",
                     "( <G>, <searchlimit> )");
     Info(InfoRCWA,1,"with <searchlimit> greater than 7.");
     return fail;
    fi;
  end );

#############################################################################
##
#M  TryIsTransitiveOnNonnegativeIntegersInSupport( <G>, <searchlimit> )
##
InstallMethod( TryIsTransitiveOnNonnegativeIntegersInSupport,
               "for an rcwa group over Z and bound on efforts (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsPosInt ], 0,

  function ( G, searchlimit )

    local  ShortOrbitsFound, cert, gens, S, range, m, orbmod,
           elms, B, r, B_act, B_act_old, r_act, b, p0;

    ShortOrbitsFound := function ( range, maxlng )
      return ShortOrbits(G,Intersection(range,Support(G)),maxlng) <> [];
    end;

    if HasIsFinite(G) and IsFinite(G) then
      SetIsTransitiveOnNonnegativeIntegersInSupport(G,false);
      return "intransitive (group is finite)";
    elif not IsSignPreserving(G) then
      SetIsTransitiveOnNonnegativeIntegersInSupport(G,false);
      return "doesn't act on N_0 (group is not sign-preserving)";
    fi;

    gens := GeneratorsOfGroup(G);
    S    := Support(G);

    for range in [[0..15],[0..63],
                  [0..Maximum(255,Lcm(List(gens,Mod)))]] do
      if ShortOrbitsFound(range,32) then
        SetIsTransitiveOnNonnegativeIntegersInSupport(G,false);
        return "intransitive (group has finite orbits)";
      fi;
    od;

    for m in DivisorsInt(Lcm(List(gens,Mod))) do
      orbmod := List(ProjectionsToInvariantUnionsOfResidueClasses(G,m),
                     pi -> Support(Image(pi)));
      if Number(orbmod,orb->Intersection(S,orb)<>[]) > 1 then
        SetIsTransitiveOnNonnegativeIntegersInSupport(G,false);
        return Concatenation("intransitive (group has > 1 orbit (mod ",
                             String(m),"))");
      fi;
    od;

    cert := TryToComputeTransitivityCertificate(G,searchlimit);

    if cert.complete = true then
      SetTransitivityCertificate(G,cert);
      SetIsTransitiveOnNonnegativeIntegersInSupport(G,true);
    elif cert.status = "intransitive, but finitely many orbits" then
      SetTransitivityCertificate(G,cert);
      SetIsTransitiveOnNonnegativeIntegersInSupport(G,false);
    elif cert.status = "finitely many orbits" then
      SetTransitivityCertificate(G,cert);
    fi;

    return cert.status;
  end );

#############################################################################
##
#M  TryToComputeTransitivityCertificate( <G>, <searchlimit> )
##
InstallMethod( TryToComputeTransitivityCertificate,
               "for rcwa groups over Z (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsPosInt ], 0,

  function ( G, searchlimit )

    local  classes, words, imgs, gens, D, S, R, F, phi, B, B_last, r, limit,
           peakbound, n, m, k, g, w, downcls, coveredcls, cl, I, c,
           smallpointbound, rem, p0, abortdensity, check, varnames, i;

    Info(InfoRCWA,1,"Invoking `TryToComputeTransitivityCertificate'",
                    " for G = ",ViewString(G));
    abortdensity := GetOption("abortdensity",0,IsPosRat);
    peakbound := GetOption("peakbound",infinity,IsPosInt);
    if not IsSignPreserving(G)
      or ForAny(ShortResidueClassOrbits(G,60,60),orb->Length(orb)>1)
    then return fail; fi;

    G := SparseRep(G);
    gens := GeneratorsOfGroup(G);
    if Length(gens) <= 26 then
      varnames := List("abcdefghijklmnopqrstuvwxyz",ch->[ch]);
      F := FreeGroup(varnames{[1..Length(gens)]});
    else F := FreeGroup(Length(gens)); fi;
    phi := GroupHomomorphismByImagesNC(F,G);
    D := []; S := Support(G);
    R := List(AsUnionOfFewClasses(S),SparseRep);
    classes := []; words := []; imgs := []; smallpointbound := 0;
    while R <> [] and Sum(List(R,Density)) > abortdensity do
      Info(InfoRCWA,1,"Remaining classes: ",ViewString(R));
      k := 0;
      repeat
        n := Residue(R[1]);
        # n must be in the support, and it must not be its
        # smallest nonnegative element:
        while not n in S do
          n := n + Modulus(R[1]);
        od;
        repeat
          n := n + Modulus(R[1]);
        until n in S;
        n := n + k * Modulus(R[1]);
        limit := 2 * n;
        Info(InfoRCWA,1,"n = ",n);
        repeat
          if IsBound(B) then B_last := B; else B_last := []; fi;
          if   limit > n
          then Info(InfoRCWA,1,"Increasing limit -- new limit = ",limit); fi;
          B := RestrictedBall(G,n,searchlimit,limit:Spheres,UntilSmaller);
          m := Minimum(Last(B));
          limit := Maximum(List(gens,Mult)) * limit
                 + Maximum(List(gens,MaximalShift));
        until m < n or (peakbound <> infinity and limit > peakbound * n)
           or B = B_last;
        if m >= n then
          return rec( phi := phi, classes := classes,
                      words := words, complete := false, remaining := R,
                      smallpointbound := smallpointbound,
                      status := "unclear" );
        fi;
        w:=RepresentativeActionPreImage(G,n,m,OnPoints,F:pointlimit:=limit);
        g:=Image(phi,w); Add(imgs,g);
        smallpointbound := Maximum(smallpointbound,MaximalShift(g));
        downcls := [];
        for c in Coefficients(g) do
          if   c[5] > c[3] or (c[5] = c[3] and c[4] < 0)
          then Add(downcls,SparseRep(ResidueClass(c[1],c[2]))); fi;
        od;
        k := k + 1;
      until ForAny(downcls,cl->n in cl);
      coveredcls := [];
      for i in [1..Length(R)] do
        for cl in downcls do
          I := Intersection(R[i],cl);
          if I <> [] then
            Append(coveredcls,List(AsUnionOfFewClasses(I),SparseRep));
            R[i] := Difference(R[i],I);
          fi;
        od;
        R[i] := List(AsUnionOfFewClasses(R[i]),SparseRep);
      od;
      R := Filtered(Set(Flat(R)),cl->cl<>[]);
      coveredcls := Set(coveredcls);
      Add(classes,coveredcls); Add(words,w);
    od;

    # check := [ R = [], Sum(List(Flat(classes),Density)) = Density(S),
    #           Union(Flat(classes)) = Union(S,ExcludedElements(S)) ];
    # if not check in [[true,true,true],[false,false,false]]
    # then Error("internal error!"); fi;

    check := [ R = [], Sum(List(Flat(classes),Density)) = Density(S) ];
    if not check in [[true,true],[false,false]]
    then Error("internal error!"); fi;

    if R <> [] then
      return rec( phi := phi, classes := classes,
                  words := words, complete := false, remaining := R,
                  smallpointbound := smallpointbound,
                  status := "unclear" );
    fi;

    if ValueOption("skipsmallpointcheck") = true then
      return rec( phi := phi, classes := classes,
                  words := words, complete := true,
                  smallpointbound := smallpointbound,
                  status := "transitive" );
    fi;

    Info(InfoRCWA,1,"Checking transitivity on moved points ",
                    "0 <= n <= ",smallpointbound," ...");
    rem := [];
    for n in [0..smallpointbound] do
      if n in S and not ForAny(imgs,g->n^g<n) then
        Add(rem,n);
      fi;
      if n mod 1000000 = 0 then
        Info(InfoRCWA,1,"n = ",n,": |rem| = ",Length(rem));
      fi;
    od;
    p0 := Minimum(rem);
    Info(InfoRCWA,1,"Computing balls about ",p0," ...");
    B := [p0]; r := 8; limit := 4 * Maximum(rem) + 1;
    repeat
      Info(InfoRCWA,1,"Checking up to radius ",r,
                      " and up to limit ",limit," ...");
      B_last := B;
      B := RestrictedBall(G,p0,r,limit);
      r := 2 * r;
      limit := 2 * limit;
      if MemoryUsage(B) > 2^24 then # 16MB; at most finitely many orbits
        return rec( phi := phi, classes := classes,
                    words := words, complete := false, remaining := R,
                    smallpointbound := smallpointbound,
                    status := "finitely many orbits" );
      fi;
    until IsSubset(B,rem) or B = B_last;
    if IsSubset(B,rem) then
      return rec( phi := phi, classes := classes,
                  words := words, complete := true,
                  smallpointbound := smallpointbound,
                  status := "transitive" );
    else # p0^G finite 
      return rec( phi := phi, classes := classes,
                  words := words, complete := false,
                  smallpointbound := smallpointbound,
                  status := "intransitive, but finitely many orbits" );
    fi;
  end );

#############################################################################
##
#M  SimplifiedCertificate( <cert> )
##
InstallMethod( SimplifiedCertificate,
               "for transitivity certificates of rcwa groups over Z (RCWA)",
               ReturnTrue, [ IsRecord ], 0,

  function ( input )

    local  output, G, S, phi, words, imgs, classes, smallpointbound,
           R, D, U, inds, indssel, down, reduced, i;

    if not IsSubset(NamesOfComponents(input), [ "status", "words", "classes",
                    "phi", "complete", "smallpointbound" ])
    then TryNextMethod(); fi;

    phi   := input.phi;
    G     := Range(phi);
    S     := Support(G);
    words := AsSortedList(input.words);
    imgs  := List(words,w->w^phi);
    if IsBound(input.remaining) then R := input.remaining; else R := []; fi;
    R     := Union(R);
    D     := SparseRep(Difference(S,R));

    inds := [1..Length(words)];
    repeat
      reduced := false;
      for i in [Length(inds),Length(inds)-1..1] do
        indssel := Difference(inds,[inds[i]]);
        if Union(List(imgs{indssel},
                      g->Union(DecreasingOn(g),ShiftsDownOn(g)))) = D
        then
          inds := indssel;
          reduced := true;
          break;
        else continue; fi;
      od;
    until not reduced;
    words := words{inds};
    imgs  := imgs{inds};

    U := D; classes := [];
    for i in [1..Length(imgs)] do
      down := Intersection(U,Union(DecreasingOn(imgs[i]),
                                   ShiftsDownOn(imgs[i])));
      Add(classes,AsUnionOfFewClasses(down));
      U := Difference(U,down);
    od;
    if U <> [] then Error("internal error"); fi;

    if input.status = "transitive" then
      smallpointbound := Maximum(Filtered([0..input.smallpointbound],
                                           n->not ForAny(imgs,g->n^g<n)));
    fi;

    output := rec( phi := phi, words := words, classes := classes,
                   complete := input.complete,
                   smallpointbound := smallpointbound,
                   status := input.status );
    return output;
  end );

#############################################################################
##
#M  TryToComputeDegreeOfTransitivity( <G>, <timelimit> )
##
InstallMethod( TryToComputeDegreeOfTransitivity,
               "for rcwa groups over Z (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsPosInt ], 0,

  function ( G, timelimit )

    local  chain, cert, deg, fixed, pts, gens, S, F, names, 
           pi, words, elms, start, n, n0;

    names := ["a","b","c","d","e","f","g","h","i","j","k","l"];
    F  := FreeGroup(names{[1..Length(GeneratorsOfGroup(G))]});
    pi := EpimorphismByGenerators(F,G);
    Print("Checking transitivity ... \n");
    cert := TryToComputeTransitivityCertificate(G,100);
    if cert = fail then
      Print("The group does not act transitively.\n");
      return 0;
    elif cert.status = "unclear" then
      Print("Failed to figure out whether the group acts transitively.\n");
      return fail;
    elif cert.status = "transitive" then
      Print("The group acts at least 1-transitively on the set of ",
            "nonnegative integers in its support.\n");
      chain := [G];
      deg   := 1;
      start := Runtime();
      repeat
        Print("Checking for ",deg+1,"-transitivity ...\n");
        n     := 0;
        fixed := [];
        while Length(fixed) < deg do
          if n in Support(G) then Add(fixed,n); fi;
          n := n + 1;
        od;
        Print("Stabilized points: ",fixed,"\n");
        while not n in Support(G) do
          n := n + 1;
        od;
        n0   := n;
        gens := [];
        S := Group(One(G));
        repeat
          repeat
            n := n + 1;
          until n in Support(G)
            and (       Density(Support(S)) = Density(Support(G))
                 or not n in Support(S));
          Print("Searching stabilizer elements mapping ",n0,
                " to ",n," ...\n");
          words := RepresentativesActionPreImage(G,Concatenation(fixed,[n0]),
                                                   Concatenation(fixed,[n]),
                                                 OnTuples,F);
          elms  := List(words,w->w^pi);
          gens := Union(gens,elms);
          Print("Number of stabilizer generators before reduction: ",
                Length(gens),"\n");
          S := Group(gens);
          gens := SmallGeneratingSet(S);
          Print("Number of stabilizer generators after reduction: ",
                Length(gens),"\n");
          S := Group(gens);
          Print("Support(S) = "); View(Support(S)); Print("\n");
          if Support(S) = Difference(Support(G),fixed) then
            cert := TryToComputeTransitivityCertificate(S,3);
          else
            cert := rec(status := "intransitive");
          fi;
          if Runtime() - start >= timelimit then
            return chain;
          fi;
        until cert <> fail and cert.status = "transitive";
        deg := deg + 1;
        Print(deg,"-transitivity established.\n");
        Add(chain,S);
      until Runtime() - start >= timelimit;
      return chain;
    else
      return fail;
    fi;
  end );

#############################################################################
##
#M  SupersetOfOrbitRepresentatives( <G>, <maxsaddle>, <maxprog> )
##
InstallMethod( SupersetOfOrbitRepresentatives,
               "for subgroups of CT(Z) (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsPosInt, IsPosInt ], 0,

  function ( G, maxsaddle, maxprog )

    local  R, R_last, R_red, D, D_red, I, words, w, g, h, c, cls, putaside,
           downcoeffs, down, fraclimit, n, k, d, B, cl, r, m, m_0, F, pi,
           gensnames, smallpointbound, rem, red, maxdist, success, stuck,
           perm, m_max, S, inds, i;

    if not IsSignPreserving(G) then TryNextMethod(); fi;

    maxdist := GetOption("maxdist",infinity,IsPosInt);
    fraclimit := GetOption("fraclimit",infinity,IsPosInt);

    gensnames := List([27..52],i->LETTERS{[i]});
    G := SparseRep(G);
    F := FreeGroup(gensnames{[1..Length(GeneratorsOfGroup(G))]});
    pi := EpimorphismByGenerators(F,G);

    R := Integers; R_last := fail;
    D := []; words := []; g := []; putaside := [];
    smallpointbound := 0;
    repeat
      stuck  := R = R_last;
      R_last := R;
      cls    := AsUnionOfFewClasses(R);
      Info(InfoRCWA,1,"cls = ",ViewString(cls));
      for cl in cls do
        Info(InfoRCWA,1,"  cl = ",ViewString(cl));
        I := Intersection(R,cl);
        if I = [] then
          Info(InfoRCWA,1," (already handled)");
          continue;
        elif Density(I) < Density(cl)/maxprog then
          Info(InfoRCWA,1,
               " (already partially handled -- skipping to next round)");
          continue;
        fi;
        r := Residue(cl); m := Mod(cl);
        k := 1; success := false;
        while k <= maxprog do
          n := k*m+r;
          d := DistanceToNextSmallerPointInOrbit(G,n:ceiling:=maxsaddle*n);
          Info(InfoRCWA,1,"    k = ",k,": d = ",d);
          if IsInt(d) and d <= maxdist then
            B := RestrictedBall(G,n,d,maxsaddle*n:Spheres);
            w := RepresentativeActionPreImage(G,n,Minimum(B[d+1]),OnPoints,
                                           F:pointlimit:=maxsaddle*n,onlyup);
            Info(InfoRCWA,1,"    w = ",w);
            h := w^pi;
            Add(words,w);
            Add(g,h);
            downcoeffs := Filtered( Coefficients( h ),
                                    c ->   (     c[3] < c[5]
                                            or ( c[3] = c[5] and c[4] < 0))
                                         and c[2] <= fraclimit * m );
            down := ResidueClassUnion( Integers,
                                       List( downcoeffs, c -> c{[1,2]} ) );
            down := Intersection(R,down);
            Add(D,down);
            Info(InfoRCWA,1,"    D = ",
                 ViewString(AsUnionOfFewClasses(down)));
            R := Difference(R,down);
            if down <> [] then success := true; fi;
            for c in downcoeffs do
              if c[4] > 0 and Int(c[4]/(c[5]-c[3])) > smallpointbound then
                smallpointbound := Int(c[4]/(c[5]-c[3]));
                Info(InfoRCWA,1,"    smallpointbound = ",smallpointbound);
              fi;
            od;
            if not stuck then
              break;
            fi;
          fi;
          k := k + 1;
        od;
        if k > maxprog and not success then
          Add(putaside,cl);
          R := SparseRep(Difference(R,cl));
        fi;
      od;
    until R = [];
    rem := [];
    for n in [0..smallpointbound] do
      if not ForAny(g,h->n^h<n) then
        Add(rem,n);
      fi;
    od;

    red   := Positions(D,[]);
    words := words{Difference([1..Length(D)],red)};
    g     := g{Difference([1..Length(D)],red)};
    D     := D{Difference([1..Length(D)],red)};
    perm  := SortingPerm(words);
    words := Permuted(words,perm);
    g     := Permuted(g,perm);
    D     := Permuted(D,perm);
    R     := Union(Difference(Integers,Union(D)),rem);

    m_max := ValueOption("optimize");
    if IsPosInt(m_max) then

      Info(InfoRCWA,1,"Trying to reduce the cover ...");

      m_0 := Lcm(List(D,Mod));
      for m in DivisorsInt(m_0) do
        if m > m_max then break; fi;
        Info(InfoRCWA,1,"  Trying m = ",m," ...");
        D_red := List(D,Di->ResidueClassUnion(Integers,
                                              Filtered(Classes(Di),
                                                       cl->m mod cl[2]=0)));
        R_red := Union(Difference(Integers,Union(D_red)),rem);
        if Density(R_red) = Density(R) then
          Info(InfoRCWA,1,"Considering residue classes with moduli dividing",
                          " ",m," suffices.");
          inds := Filtered([1..Length(D)],i->D_red[i]<>[]);
          repeat
            k := Length(inds) + 1;
            repeat
              k := k - 1;
              S := Integers;
              for i in [1..Length(inds)] do
                if i <> k then
                  S := Difference(S,D_red[inds[i]]);
                fi;
              od;
            until k < 1 or Density(S) <= Density(R);
            if k >= 1 then
              Info(InfoRCWA,1,"Element at position ",inds[k],
                              " is redundant.");
              inds := Difference(inds,inds{[k]});
            fi;
          until k = 0;
          words := words{inds};
          g     := g{inds};
          D     := D{inds};
          R     := Union(Difference(Integers,Union(D)),rem);
          break;
        fi;
      od;
    
    fi;

    return rec( R := R, D := D, w := words, g := g, pi := pi );

  end );

#############################################################################
##
#M  DistanceToNextSmallerPointInOrbit( <G>, <n> ) . .  for rcwa groups over Z
##
InstallMethod( DistanceToNextSmallerPointInOrbit,
               "for an rcwa group over Z and an integer (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsInt ], 0,

  function ( G, n )

    local  B, m, ceiling;

    if n = 0 or not n in Support(G) then return infinity; fi;

    ceiling := ValueOption("ceiling");
    if IsPosInt(ceiling) then
      B := RestrictedBall(G,n,ceiling,ceiling:UntilSmaller,Spheres);
    else
      B := Ball(G,n,2^60-1:UntilSmaller,Spheres); # 2^60-1 = max.-lng./64bit
    fi;
    B := Filtered(B,S->S<>[]);
    m := Minimum(List(Last(B),AbsInt));
    if m < AbsInt(n) then
      if   ValueOption("alsopoint") = true
      then return [ Length(B) - 1, m ];
      else return Length(B) - 1; fi;
    else
      if IsPosInt(ceiling) then
        Info(InfoRCWA,3,"DistanceToNextSmallerPointInOrbit: ");
        Info(InfoRCWA,3,"'ceiling': chosen value might be too small, ",
                        "you might try a larger one ...");
        return fail;
      else
        return infinity;
      fi;
    fi;
  end );

#############################################################################
##
#M  CollatzLikeMappingByOrbitTree( <G>, <root>, <min_r>, <max_r> )
##
InstallMethod( CollatzLikeMappingByOrbitTree,
               "for rcwa group over Z, point and search radius (RCWA)",
               ReturnTrue,
               [ IsRcwaGroupOverZ, IsInt, IsPosInt, IsPosInt ], 0,

  function ( G, root, min_r, max_r )

    local  B, r, f, c, gens, mods, affs, maps, imgs,
           affsmaps, affsimgs, viable, aff, n, m;

    if min_r > max_r then return fail; fi;
    gens := List(GeneratorsOfGroup(G),SparseRep);
    B := Ball(G,root,max_r,OnPoints:Spheres);
    Info(InfoRCWA,2,"CollatzLikeMappingByOrbitTree: sphere sizes = ",
                    List(B,Length));
    if [] in B then
      Info(InfoRCWA,1,"CollatzLikeMappingByOrbitTree: ",
                      "the orbit is finite.");
      return fail;
    fi;
    affs := Filtered(Union(List(gens,Coefficients)),c->c{[3..5]}<>[1,0,1]);
    affsmaps := []; affsimgs := [];
    for r in [min_r+1..max_r+1] do
      Info(InfoRCWA,3,"Computing mappings and images for r = ",r," ...");
      for n in B[r] do
        maps := Filtered(affs,c->n mod c[2] = c[1]
                                 and (c[3]*n+c[4])/c[5] in B[r-1]);
        imgs := Set(maps,c->(c[3]*n+c[4])/c[5]);
        if imgs = [] then Error("internal error"); return fail; fi;
        if Length(imgs) > 1 then
          Info(InfoRCWA,1,"CollatzLikeMappingByOrbitTree: ",
                          "the orbit is not treelike.");
          return fail;
        fi;
        Add(affsmaps,[n,maps]);
        Add(affsimgs,[n,imgs]);
      od;
    od;
    mods := Filtered(AllSmoothIntegers(PrimeSet(G),Length(affsmaps)),m->m>1);
    for m in mods do
      Info(InfoRCWA,3,"Checking modulus m = ",m," ...");
      c := List([0..m-1],
                r->Set(Filtered(affsmaps,s->s[1] mod m = r),
                                     t->t[2][1]{[3..5]}));
      if [] in c or ForAny(c,alts->Length(alts)>1) then continue; fi;
      c := List(c,t->t[1]);
      f := RcwaMapping(c:BeQuiet);
      if f <> fail then
        viable := [];
        for aff in affs do
          for r in [0..m-1] do
            if aff{[3..5]} = c[r+1] then
              viable := Union(viable,Intersection(ResidueClass(aff[1],aff[2]),
                                                  ResidueClass(r,m)));
            fi;
          od;
        od;
        if IsIntegers(viable) then return SparseRep(f); fi;
        f := PiecewiseMapping([viable,Difference(Integers,viable)],
                              [f,One(f)]);
        return f;
      else continue; fi;
    od;
    Info(InfoRCWA,1,"CollatzLikeMappingByOrbitTree: nothing found.");
    return fail;
  end );

#############################################################################
##
#S  Testing for primitivity. ////////////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#M  IsPrimitive( <G>, <S> ) . . . for an rcwa group and a residue class union
##
##  This method might fail or run into an infinite loop.
##
InstallMethod( IsPrimitive,
               "for an rcwa group and a residue class union (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsListOrCollection ], 0,

  function ( G, S )
    if not IsSubset(Source(One(G)),S) then TryNextMethod(); fi;
    if   not ForAll(GeneratorsOfGroup(G),g->S^g=S)
    then Error("IsPrimitive: <G> must act on <S>.\n"); fi;
    if not IsTransitive(G,S) or IsTame(G) then return false; fi;
    TryNextMethod();
  end );

#############################################################################
##
#S  Orbits of rcwa groups. //////////////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#V  IsRcwaGroupOrbitInStandardRep . .  shorthand for rcwa group orbit objects
##
BindGlobal( "IsRcwaGroupOrbitInStandardRep",
             IsRcwaGroupOrbit and IsRcwaGroupOrbitStandardRep );

#############################################################################
##
#M  OrbitOp( <G>, <pnt>, <gens>, <acts>, <act> )
##
InstallOtherMethod( OrbitOp,
                    "for tame rcwa groups over Z (RCWA)", ReturnTrue,
                    [ IsRcwaGroupOverZ, IsInt, IsList, IsList, IsFunction ],
                    0,

  function ( G, pnt, gens, acts, act )

    local  P, K, where, gensrests, noncwoppos, m, S, orbs, i;

    if act <> OnPoints then TryNextMethod(); fi;
    orbs := ShortOrbits(G,[pnt],100);
    if orbs <> [] then return orbs[1]; fi;
    if IsFinite(G) or not IsTame(G) then TryNextMethod(); fi;
    P          := RespectedPartition(G);
    K          := KernelOfActionOnRespectedPartition(G);
    where      := First(P,cl->pnt in cl);
    gensrests  := List(GeneratorsOfGroup(K),g->RestrictedPerm(g,where));
    noncwoppos := Filtered([1..Length(gensrests)],
                           i->not IsClassWiseOrderPreserving(gensrests[i]));
    for i in noncwoppos{[2..Length(noncwoppos)]} do
      gensrests[i] := gensrests[i] * gensrests[noncwoppos[1]];
    od;
    m :=   Gcd(List(Filtered(gensrests,IsClassWiseOrderPreserving),
                    Determinant))
         * Modulus(where);
    S := ResidueClass(Integers,m,pnt);
    if   not IsEmpty(noncwoppos)
    then S := Union(S,S^gensrests[noncwoppos[1]]); fi;
    return OrbitUnion(G,S);
  end );

#############################################################################
##
#M  OrbitOp( <G>, <pnt>, <gens>, <acts>, <act> )  for wild rcwa groups over Z
##
InstallOtherMethod( OrbitOp,
                    "for wild rcwa groups over Z (RCWA)", ReturnTrue,
                    [ IsRcwaGroupOverZ, IsInt, IsList, IsList, IsFunction ],
                    0,

  function ( G, pnt, gens, acts, act )

    local  orbit, ball, oldball, giveup;

    if IsTame(G) and act = OnPoints then TryNextMethod(); fi;

    giveup := ValueOption("giveup");
    if not IsPosInt(giveup) then giveup := 1000; fi;

    gens  := Union(gens,List(gens,Inverse));
    ball  := [pnt];
    repeat    
      oldball := ShallowCopy(ball);
      ball    := Union(oldball,
                       Union(List(gens,gen->List(oldball,pt->act(pt,gen)))));
    until ball = oldball or Length(ball) > giveup;
    if ball = oldball then return Set(ball); fi;
    
    orbit := Objectify( NewType( CollectionsFamily( FamilyObj( pnt ) ),
                                 IsRcwaGroupOrbitInStandardRep ),
                        rec( group          := G,
                             representative := pnt,
                             action         := act ) );
    return orbit;
  end );

#############################################################################
##
#M  UnderlyingGroup( <orbit> ) . . . . . . . . . .  for orbits of rcwa groups
##
InstallMethod( UnderlyingGroup, "for orbits of rcwa groups (RCWA)", true,
               [ IsRcwaGroupOrbitInStandardRep ], 0,
               orbit -> orbit!.group );

#############################################################################
##
#M  Representative( <orbit> ) . . . . . . . . . . . for orbits of rcwa groups
##
InstallMethod( Representative, "for orbits of rcwa groups (RCWA)", true,
               [ IsRcwaGroupOrbitInStandardRep ], 0,
               orbit -> orbit!.representative );

#############################################################################
##
#M  IsSubset( <R>, <orbit> ) . for underlying ring and orbit of an rcwa group
##
InstallMethod( IsSubset,
               "for underlying ring and orbit of an rcwa group (RCWA)",
               ReturnTrue, [ IsRing, IsRcwaGroupOrbitInStandardRep ], 0,

  function ( R, orbit )
    if   IsRcwaGroup(orbit!.group) and IsSubset(R,Source(One(orbit!.group)))
    then return true;
    elif not orbit!.representative in R then return false;
    else TryNextMethod(); fi;
  end );

#############################################################################
##
#M  ViewObj( <orbit> ) . . . . . . . . . . . . . .  for orbits of rcwa groups
##
InstallMethod( ViewObj,
               "for orbits of rcwa groups (RCWA)", true,
              [ IsRcwaGroupOrbitInStandardRep ], 0,

  function ( orbit )
    Print("<orbit of ");
    ViewObj(orbit!.representative);
    Print(" under ");
    ViewObj(orbit!.group);
    Print(">");
  end );

#############################################################################
##
#M  \=( <orbit1>, <orbit2> ) . . . . . . . . . . .  for orbits of rcwa groups
##
InstallMethod( \=,
               "for orbits of rcwa groups (RCWA)", IsIdenticalObj,
               [ IsRcwaGroupOrbitInStandardRep,
                 IsRcwaGroupOrbitInStandardRep ], 0,

  function ( orbit1, orbit2 )

    local  G, gens, act, reps,
           m, orbs_mod_m, orbs_mod_m_tested,
           balls, oldballs;

    if   orbit1!.group  <> orbit2!.group
      or orbit1!.action <> orbit2!.action
    then return Set(AsList(orbit1)) = Set(AsList(orbit2)); fi;

    G    := orbit1!.group;
    gens := Set(GeneratorsAndInverses(G));
    act  := orbit1!.action;
    reps := [orbit1!.representative,orbit2!.representative];

    balls             := [[reps[1]],[reps[2]]];
    orbs_mod_m_tested := false;
    repeat
      oldballs := List(balls,ShallowCopy);
      balls    := List([1..2],i->Union(oldballs[i],
                       Union(List(gens,gen->List(oldballs[i],
                                                 pt->act(pt,gen))))));
      if   balls[1] = oldballs[1] and Length(balls[2]) > Length(balls[1])
        or balls[2] = oldballs[2] and Length(balls[1]) > Length(balls[2])
      then return false; fi;
      if    not orbs_mod_m_tested
        and act = OnPoints and IsSubset(Source(One(G)),reps)
        and Maximum(List(balls,Length)) > Lcm(List(gens,Modulus))^2
      then
        m          := Lcm(List(gens,Modulus));
        orbs_mod_m := OrbitsModulo(G,m);
        if    First(orbs_mod_m,orb->reps[1] mod m in orb)
           <> First(orbs_mod_m,orb->reps[2] mod m in orb)
        then return false; fi;
        orbs_mod_m_tested := true;
      fi;
    until not IsEmpty(Intersection(balls));
    return true;
  end );

#############################################################################
##
#M  \=( <orbit>, <list> ) . . . . .  for an orbit of an rcwa group and a list
##
InstallMethod( \=,
               "for an orbit of an rcwa group and a list (RCWA)", ReturnTrue,
               [ IsRcwaGroupOrbitInStandardRep, IsList ], 0,

  function ( orbit, list )

    local  ball, act, gens;

    gens := Set(GeneratorsAndInverses(orbit!.group));
    act  := orbit!.action;
    ball := [orbit!.representative];
    repeat    
      ball := Union(ball,Union(List(gens,gen->List(ball,pt->act(pt,gen)))));
      if   ball = list             then return true;
      elif not IsSubset(list,ball) then return false; fi;
    until Length(ball) > Length(list);
    return false;
  end );

#############################################################################
##
#M  \=( <list>, <orbit> ) . . . . .  for a list and an orbit of an rcwa group
##
InstallMethod( \=,
               "for a list and an orbit of an rcwa group (RCWA)", ReturnTrue,
               [ IsList, IsRcwaGroupOrbitInStandardRep ], 0,
               function ( list, orbit ) return orbit = list; end );

#############################################################################
##
#M  AsList( <orbit> ) . . . . . . . . . . . . . . . for orbits of rcwa groups
##
InstallMethod( AsList,
               "for orbits of rcwa groups (RCWA)", true,
               [ IsRcwaGroupOrbitInStandardRep ], 0,

  function ( orbit )

    local  ball, oldball, gens, act;

    gens := Set(GeneratorsAndInverses(orbit!.group));
    act  := orbit!.action;
    ball := [orbit!.representative];
    repeat    
      oldball := ShallowCopy(ball);
      ball    := Union(oldball,
                       Union(List(gens,gen->List(oldball,pt->act(pt,gen)))));
    until ball = oldball;
    return Set(ball);
  end );

#############################################################################
##
#M  \in( <point>, <orbit> ) . . . . for a point and an orbit of an rcwa group
##
InstallMethod( \in,
               "for a point and an orbit of an rcwa group (RCWA)",
               ReturnTrue, [ IsObject, IsRcwaGroupOrbitInStandardRep ], 0,

  function ( point, orbit )
    return Orbit(orbit!.group,point,orbit!.action) = orbit;
  end );

#############################################################################
##
#M  Size( <orbit> ) . . . . . . . . . . . . . . . . for orbits of rcwa groups
##
InstallMethod( Size,
               "for orbits of rcwa groups (RCWA)", true,
               [ IsRcwaGroupOrbitInStandardRep ], 0,

  function ( orbit )
    if HasIsFinite(orbit) and not IsFinite(orbit) then return infinity; fi;
    return Length(AsList(orbit));
  end );

#############################################################################
##
#M  Iterator( <orbit> ) . . . . . . . . . . . . . . for orbits of rcwa groups
##
InstallMethod( Iterator,
               "for orbits of rcwa groups (RCWA)", true,
               [ IsRcwaGroupOrbitInStandardRep ], 0,

  function ( orbit )
    return Objectify( NewType( IteratorsFamily,
                               IsIterator
                           and IsMutable
                           and IsRcwaGroupOrbitsIteratorRep ),
                      rec( orbit     := orbit,
                           sphere    := [Representative(orbit)],
                           oldsphere := [],
                           pos       := 1 ) );
  end );

#############################################################################
##
#M  NextIterator( <iter> ) . . . . . . for iterators of orbits of rcwa groups
##
InstallMethod( NextIterator,
               "for iterators of orbits of rcwa groups (RCWA)", true,
               [     IsIterator and IsMutable
                 and IsRcwaGroupOrbitsIteratorRep ], 0,

  function ( iter )

    local  orbit, G, action, gens, sphere, point;

    if not HasGeneratorsOfGroup(iter!.orbit!.group) then TryNextMethod(); fi;

    orbit  := iter!.orbit;
    G      := orbit!.group;
    action := orbit!.action;
    gens   := GeneratorsAndInverses(G);
    point  := iter!.sphere[iter!.pos];
    if iter!.pos < Length(iter!.sphere) then iter!.pos := iter!.pos + 1; else
      sphere := Difference(Union(List(gens,g->List(iter!.sphere,
                                                   pt->action(pt,g)))),
                           Union(iter!.sphere,iter!.oldsphere));
      iter!.oldsphere := iter!.sphere;
      iter!.sphere    := sphere;
      iter!.pos       := 1;
    fi;
    return point;
  end );

#############################################################################
##
#M  IsDoneIterator( <iter> ) . . . . . for iterators of orbits of rcwa groups
##
InstallMethod( IsDoneIterator,
               "for iterators of orbits of rcwa groups (RCWA)", true,
               [ IsIterator and IsRcwaGroupOrbitsIteratorRep ], 0,
               iter -> IsEmpty( iter!.sphere ) );

#############################################################################
##
#M  ShallowCopy( <iter> ) . . . . . .  for iterators of orbits of rcwa groups
##
InstallMethod( ShallowCopy,
               "for iterators of orbits of rcwa groups (RCWA)", true,
               [ IsIterator and IsRcwaGroupOrbitsIteratorRep ], 0,

  iter -> Objectify( Subtype( TypeObj( iter ), IsMutable ),
                     rec( orbit     := iter!.orbit,
                          sphere    := iter!.sphere,
                          oldsphere := iter!.oldsphere,
                          pos       := iter!.pos ) ) );

#############################################################################
##
#M  ViewObj( <iter> ) . . . . . . . .  for iterators of orbits of rcwa groups
##
InstallMethod( ViewObj,
               "for iterators of orbits of rcwa groups (RCWA)", true,
               [ IsIterator and IsRcwaGroupOrbitsIteratorRep ], 0,

  function ( iter )
    Print("Iterator of ");
    ViewObj(iter!.orbit);
  end );

#############################################################################
##
#M  GrowthFunctionOfOrbit( <orb>, <r_max>, <size_max> ) . . for orbit objects
##
InstallMethod( GrowthFunctionOfOrbit,
               "for rcwa group orbit objects (RCWA)", ReturnTrue,
               [ IsRcwaGroupOrbit, IsPosInt, IsPosInt ], 0,

  function ( orb, r_max, size_max )
    return GrowthFunctionOfOrbit( UnderlyingGroup( orb ),
                                  Representative( orb ),
                                  r_max, size_max );
  end );

#############################################################################
##
#M  GrowthFunctionOfOrbit( <G>, <n>, <r_max>, <size_max> ) .  for rcwa groups
##
InstallMethod( GrowthFunctionOfOrbit,
               "for rcwa groups (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsObject, IsPosInt, IsPosInt ], 0,

  function ( G, n, r_max, size_max )

    local  spheresizes, gens, S_last, S, S_next, r,
           smallpoints, small, maxsize, notify;

    if not n in Source(One(G)) then TryNextMethod(); fi;
    small := ValueOption("small"); smallpoints := [];
    if IsList(small) and n in small then Add(smallpoints,n); fi;
    notify := ValueOption("notify");
    gens := GeneratorsOfGroup(G);
    spheresizes := [1]; S_last := []; S := [n]; r := 0; maxsize := 1;
    repeat
      r      := r + 1;
      S_next := Union(List(gens,g->S^g));
      S_next := Difference(S_next,Union(S,S_last));
      S_last := S;
      S      := S_next;
      Add(spheresizes,Length(S));
      if IsList(small) then
        smallpoints := Union(smallpoints,Filtered(S,p->p in small));
      fi;
      if IsPosInt(notify) then
        if notify > 100 and Length(S) > maxsize then
          maxsize := Length(S);
          Print("n = ",n,", r = ",r,": new sphere size record |S| = ",
                maxsize,"\n");
        fi;
        if r mod notify = 0 then
          Print("n = ",n,", r = ",r,", |S| = ",Length(S),
                ", max(S) has ",LogInt(Maximum(List(S,AbsInt)),2)+1,
                " binary digits\n");
        fi;
      fi;
    until r >= r_max or Length(S) > size_max;
    if IsList(small) then
      return rec( spheresizes := spheresizes,
                  smallpoints := smallpoints );
    else return spheresizes; fi;
  end );

#############################################################################
## 
#M  OrbitUnion( <G>, <S> ) . . . . . . . . . . .  for an rcwa group and a set
##
##  This method might run into an infinite loop.
## 
InstallMethod( OrbitUnion,
               "for an rcwa group and a set (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsListOrCollection ], 0,

  function ( G, S )

    local  R, gen, image, oldimage, g;

    if InfoLevel(InfoRCWA) >= 1 then
      Print("#I  OrbitUnion: initial set = "); ViewObj(S); Print("\n");
    fi;
    R     := Source(One(G));
    gen   := GeneratorsAndInverses(G);
    image := S;
    repeat
      oldimage := image;
      for g in gen do image := Union(image,ImagesSet(g,image)); od;
      if InfoLevel(InfoRCWA) >= 2 then
        Print("#I  Image = "); ViewObj(image); Print("\n");
      fi;
    until image = R or image = oldimage;
    return image;
  end );

#############################################################################
##
#F  CyclesOnFiniteOrbit( <G>, <g>, <n> ) . . . cycles of <g> on orbit <n>^<G>
##
InstallMethod( CyclesOnFiniteOrbit,
               "general method (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsRcwaMapping and IsBijective, IsObject ], 0,

  function ( G, g, n )

    local  orb, r, cycs, cyc, m;

    if   Source(One(G)) <> Source(g) or not n in Source(g)
    then TryNextMethod(); fi;

    r := 2;
    repeat
      r := 2 * r;
      orb := Ball(G,n,r,OnPoints:Spheres);
    until Last(orb) = [];
    orb := Union(orb);
    cycs := [];
    repeat
      m := orb[1];
      cyc := Cycle(g,m);
      Add(cycs,cyc);
      orb := Difference(orb,cyc);
    until orb = [];
    return cycs;
  end );

#############################################################################
## 
#M  ShortOrbits( <G>, <S>, <maxlng> ) . . . . . . . .  for rcwa groups over Z
## 
InstallMethod( ShortOrbits,
               "for rcwa groups over Z (RCWA)", ReturnTrue,
               [ IsRcwaGroupOverZ, IsList, IsInt ], 0,

  function ( G, S, maxlng )

    local  gens, coeffs, coeff, orbs, orb, size, max, remaining, ceiling,
           spheres, sphere, lastsphere, nextsphere, g, c, m, n0, n, i;

    max := Maximum(S);
    if S <> [0..max] then TryNextMethod(); fi;

    ceiling := ValueOption("ceiling");

    gens := Set(GeneratorsAndInverses(StandardRep(G)));
    coeffs := List(gens,Coefficients);

    orbs := []; remaining := ListWithIdenticalEntries(max+1,true); n0 := 0;
    while true do
      n0 := Position(remaining,true);
      if n0 = fail then break; else n0 := n0 - 1; fi;
      lastsphere := []; sphere := [n0];
      spheres := [sphere]; size := 1;
      repeat
        nextsphere := [];
        for i in [1..Length(coeffs)] do
          coeff := coeffs[i]; m := Length(coeff);
          for n in sphere do
            c := coeff[n mod m + 1];
            Add(nextsphere,(c[1]*n+c[2])/c[3]);
          od;
        od;
        nextsphere := Difference(nextsphere,sphere);
        nextsphere := Difference(nextsphere,lastsphere);
        if IsPosInt(ceiling) then
          if   nextsphere <> [] and Maximum(nextsphere) > ceiling
          then break; fi;
        fi;
        lastsphere := sphere;
        sphere := nextsphere;
        Add(spheres,sphere);
        size := size + Length(sphere);
      until size > maxlng or sphere = [];
      orb := Union(spheres);
      for n in orb do
        if n >= 0 and n <= max then remaining[n+1] := false; fi;
      od;
      if sphere = [] and size <= maxlng then Add(orbs,orb); fi;
    od;
    return orbs;
  end );

#############################################################################
## 
#M  ShortOrbits( <G>, <S>, <maxlng>, <maxn> ) . . . . . . . . for rcwa groups
## 
InstallMethod( ShortOrbits,
               Concatenation("for an rcwa group over Z, a set and ",
                             "two positive integers (RCWA)"), ReturnTrue,
               [ IsRcwaGroupOverZ, IsList, IsPosInt, IsPosInt ], 0,

  function ( G, S, maxlng, maxn )
    return ShortOrbits(G,S,maxlng:ceiling:=maxn);
  end );

#############################################################################
##
#M  ShortResidueClassOrbits( <G>, <modulusbound>, <maxlng> )
##
InstallMethod( ShortResidueClassOrbits,
               "for an rcwa group over Z and 2 positive integers (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsPosInt, IsPosInt ], 0,

  function ( G, modulusbound, maxlng )

    local  orbits, orbit, sphere, cl, covered, B, B_old,
           gens, affsrc, U, m, p, r, i, j;

    gens   := GeneratorsOfGroup(G);
    affsrc := List(gens,LargestSourcesOfAffineMappings);

    orbits := []; covered := [];
    for m in DivisorsInt(modulusbound) do
      Info(InfoRCWA,2,"ShortResidueClassOrbits: checking modulus m = ",m);
      for p in Difference([0..m-1],covered) do
        r := 0; B := [p];
        repeat
          r     := r + 1;
          B_old := B;
          B     := Ball(G,p,r,OnPoints);
        until B = B_old or Length(B) > maxlng;
        if Length(B) > maxlng then continue; fi;
        if p = Minimum(B) then
          orbit := []; cl := ResidueClass(p,m);
          if ForAny([1..Length(gens)],
                    i->m mod Mod(gens[i])<>0 and
                       Number(affsrc[i],src->Intersection(src,cl)<>[]) > 1)
          then continue; fi;
          sphere := [cl];
          orbit  := sphere;
          repeat 
            sphere := Difference(Union(List(gens,g->sphere^g)),orbit);
            orbit := Union(orbit,sphere);
          until sphere = [] or not ForAll(sphere,IsResidueClass);
          if sphere = [] then
            Add(orbits,orbit);
            covered := Union(covered,Union(orbit));
          fi;
        fi;
      od;
    od;

    for i in [2..Length(orbits)] do
      for j in [1..i-1] do
        if Intersection(orbits[i][1],orbits[j][1]) <> [] then
          if Mod(orbits[i][1]) > Mod(orbits[j][1]) then
            U := Union(orbits[i]);
            orbits[j] := List(orbits[j],cl->Difference(cl,U));
          else
            U := Union(orbits[j]);
            orbits[i] := List(orbits[i],cl->Difference(cl,U));
          fi;
        fi;
      od;
    od;

    if not ForAll(orbits,orb->ForAll(orb,IsResidueClass)) then
      orbits := Orbits(G,Flat(List(Concatenation(orbits),
                                   AsUnionOfFewClasses)));
    fi;

    return orbits;
  end );

#############################################################################
##
#M  ShortResidueClassOrbits( <G>, <modulusbound>, <maxlng> )
##
InstallMethod( ShortResidueClassOrbits,
              "for an rcwa group over Z and 2 positive integers, new (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsPosInt, IsPosInt ], 15,

  function ( G, modulusbound, maxlng )

    local  orbit, orbits, gens, coeffs, moduli,
           primes, primes_multdiv, primes_onlymod,
           powers_impossible, orb, cl, R, covered, pointorbs, pointorb,
           B, startmodind, m, n, r, i, j, k, p;

    orbit := function ( cl0 )

      local  B, r, cl, img, inter, c, i, j;

      B := [[cl0]];
      r := 0;
      repeat
        r := r + 1;
        Add(B,[]);
        for i in [1..Length(B[r])] do
          for j in [1..Length(coeffs)] do
            cl := B[r][i];
            inter := Filtered(coeffs[j],
                              c->(cl[1]-c[1]) mod Gcd(cl[2],c[2]) = 0);
            if    Length(inter) > 1
              and Length(Set(List(inter,c->c{[3..5]}))) > 1
            then return fail; fi;
            c := inter[1];
            img := [(c[3]*cl[1]+c[4])/c[5],c[3]*cl[2]/c[5]];
            img[1] := img[1] mod img[2];
            if img in B[r] or (r > 1 and img in B[r-1]) then continue; fi; 
            Add(B[r+1],img);
          od;
        od;
        B[r+1] := Immutable(Set(B[r+1]));
        IsSSortedList(B[r+1]);
        if Sum(List(B,Length)) > maxlng then return fail; fi;
      until B[r+1] = [];
      return Concatenation(B);
    end;

    if   ValueOption("classic") = true
      or ValueOption("classic_orbits") = true
    then TryNextMethod(); fi;

    if IsTrivial(G) then return [ [ Integers ] ]; fi;

    Info(InfoRCWA,1,"`ShortResidueClassOrbits': preprocessing ...");
    G := SparseRep(G);
    gens := Set(GeneratorsAndInverses(G));
    coeffs := List(gens,g->ShallowCopy(Coefficients(g)));
    for i in [1..Length(coeffs)] do
      Sort(coeffs[i],function(c1,c2)
                       return c1{[2,1]} < c2{[2,1]};
                     end);
    od;
    primes := PrimeSet(G);
    primes_multdiv := Union(List(gens,g->Set(Factors(Mult(g)*Div(g)))));
    primes_multdiv := Difference(primes_multdiv,[1]);
    primes_onlymod := Difference(primes,primes_multdiv);
    m := Lcm(List(gens,Mod));

    moduli := ValueOption("moduli");
    if moduli = fail then
      powers_impossible := List(primes_onlymod,p->p^(PValuation(m,p)+1));
      moduli    := AllSmoothIntegers(primes,modulusbound);
      moduli    := Filtered(moduli,m->ForAll(powers_impossible,q->m mod q<>0));
    fi;

    if ValueOption("DontUsePointOrbits") = true then
      covered := ListWithIdenticalEntries(modulusbound,false);
    else
      covered := ListWithIdenticalEntries(modulusbound,true);
      pointorbs := ShortOrbits(G,[0..modulusbound-1],maxlng);
      for orb in pointorbs do
        for n in orb do
          if   n < modulusbound and Length(orb) > 1
          then covered[n+1] := false; fi;
        od;
      od;
    fi;

    R := ValueOption("OrbitRepresentatives");
    if R <> fail then
      for n in [0..modulusbound-1] do
        if not n in R then covered[n+1] := true; fi;
      od;
    fi;

    orbits := ShallowCopy(ValueOption("known"));
    if orbits = fail then
      orbits := List(AsUnionOfFewClasses(Difference(Integers,Support(G))),
                     cl->[[Residue(cl),Modulus(cl)]]);
    else
      for orb in orbits do
        for cl in orb do
          m := Modulus(cl); r := Residue(cl);
          if r >= modulusbound then continue; fi;
          n := r;
          repeat
            covered[n+1] := true;
            n := n + m;
          until n >= modulusbound;
        od;
      od;
    fi;

    n := -1; startmodind := 1;
    repeat
      repeat
        n := n + 1;
      until n >= modulusbound or not covered[n+1];
      if n = modulusbound then break; fi;
      Info(InfoRCWA,2,"Testing n = ",n," ...");
      i := startmodind - 1; orb := fail;
      repeat
        i := i + 1;
        m := moduli[i];
        if m <= n then
          if i > startmodind then startmodind := i; fi;
          continue;
        fi; 
        orb := orbit([n mod m,m]);
      until orb <> fail or i = Length(moduli);
      if orb = fail then
        if IsBound(pointorbs) then # if point orbits have been precomputed
          pointorb := First(pointorbs,ptorb->n in ptorb);
          for j in pointorb do
            if j < modulusbound then covered[j+1] := true; fi;
          od;
        else
          B := RestrictedBall(G,n,maxlng,modulusbound-1);
          for j in B do
            if j >= 0 and j < modulusbound then covered[j+1] := true; fi;
          od;
        fi;
      else
        orb := Set(orb);
        Add(orbits,orb);
        for cl in orb do
          if cl[1] >= modulusbound then continue; fi;
          for i in [0..Int(modulusbound/cl[2])-1] do
            covered[cl[1]+i*cl[2]+1] := true;
          od;
        od;
        Info(InfoRCWA,1,"Found orbit for ",n mod m,"(",m,") of length ",
             Length(orb),".\n");
      fi;
      if Length(orbits) > Length(Set(orbits)) then Error(); fi;
    until n >= modulusbound;
    orbits := List(orbits,orb->Set(List(orb,ResidueClass)));
    Sort(orbits,function(orb1,orb2)
                  return [Length(orb1),orb1] < [Length(orb2),orb2];
                end);
    return orbits;
  end );

#############################################################################
##
#M  FixedResidueClasses( <G>, <maxmod> )
##
InstallMethod( FixedResidueClasses,
               "for an rcwa group over Z and a positive integer (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsPosInt ], 0,

  function ( G, maxmod )
    return Intersection(List(GeneratorsOfGroup(G),
                             g->FixedResidueClasses(g,maxmod)));
  end );

#############################################################################
##
#M  GuessOrbitRepresentatives( <G>, <maxrep>, <maxrad> )
##
InstallMethod( GuessOrbitRepresentatives,
               "for a subgroup of CT(Z) and two positive integers (RCWA)",
               ReturnTrue, [ IsRcwaGroupOverZ, IsPosInt, IsPosInt ], 0,

  function ( G, maxrep, maxrad )

    local  reps, rem, n, B;

    if not IsSignPreserving(G) then TryNextMethod(); fi;

    reps := [];
    rem  := [0..maxrep];
    while rem <> [] do
      n := rem[1];
      Add(reps,n);
      B := Ball(G,n,maxrad:Spheres);
      rem := Difference(rem,Union(B));
    od;

    return reps;
  end );

#############################################################################
##
#F  DrawOrbitPicture( <G>, <p0>, <bound>, <height>, <width>, <colored>,
#F                    <palette>, <filename> )
##
InstallGlobalFunction( DrawOrbitPicture,

  function ( G, p0, bound, height, width, colored, palette, filename )

    local  grid, orbits, orbit, balls, spheres, sphere, action, ps, p, k,
           color, white, z, e, offset, i, j;

    if   not (IsRcwaGroupOverZ(G) or IsRcwaGroupOverZxZ(G)) or not IsList(p0)
      or not ForAll(Flat(p0),IsInt)
      or not ForAll([bound,height,width],IsPosInt)
      or not IsBool(colored) or (colored = true and (not IsList(palette)
      or not ForAll(palette,IsList) or not Set(List(palette,Length)) = [3]
      or not IsSubset([0..255],Flat(palette)))) or not IsString(filename)
    then Error("DrawOrbitPicture: For usage, see manual.\n"); fi;

    if   IsRcwaGroupOverZ(G)   then action := OnTuples;
    elif IsRcwaGroupOverZxZ(G) then action := OnPoints; fi;

    offset := [1,1];
    if colored then
      white := 2^24-1;
      grid  := List([1..height],i->List([1..width],j->white));
      if IsInt(p0[1]) then # One orbit, color reflects distance from p0.
        spheres := RestrictedBall(G,p0,bound,action,height+width:Spheres);
        if IsRcwaGroupOverZ(G) then
          if   ForAny(spheres,sphere->sphere<>[] and Minimum(Flat(sphere))<0)
          then offset := [Int(height/2)+1,Int(width/2)+1]; fi;
        elif IsRcwaGroupOverZxZ(G) then
          if   ForAny(spheres,sphere->sphere<>[]
                              and ForAny(sphere,p->Minimum(p)<0))
          then offset := [Int(height/2)+1,Int(width/2)+1]; fi;
        fi;
        for k in [1..Length(spheres)] do
          sphere := spheres[k];
          color  := palette[(k-1) mod Length(palette) + 1];
          for p in sphere do
            i := p[1]+offset[1]; j := p[2]+offset[2];
            if   i in [1..height] and j in [1..width]
            then grid[i][j] := color*[65536,256,1]; fi;
          od;
        od;
      else # Multiple orbits, color reflects orbit membership.
        ps := p0; orbits := [];
        while ps <> [] do
          p0    := ps[1];
          orbit := RestrictedBall(G,p0,bound,action,height+width);
          ps    := Difference(ps,orbit);
          Add(orbits,orbit);
        od;
        if   Minimum(Flat(Union(orbits))) < 0
        then offset := [Int(height/2)+1,Int(width/2)+1]; fi;
        for i in [1..Length(orbits)] do
          orbit := orbits[i];
          color := palette[(i-1) mod Length(palette) + 1];
          for p in orbit do
            i := p[1]+offset[1]; j := p[2]+offset[2];
            if   i in [1..height] and j in [1..width]
            then grid[i][j] := color*[65536,256,1]; fi;
          od;
        od;
      fi;
      SaveAsBitmapPicture(grid,filename);
    else
      z := Zero(GF(2)); e := One(GF(2));
      grid := List([1..height],i->List([1..width],j->e));
      for i in [1..height] do ConvertToGF2VectorRep(grid[i]); od;
      orbit := RestrictedBall(G,p0,bound,action,height+width);
      if   Minimum(Flat(orbit)) < 0
      then offset := [Int(height/2)+1,Int(width/2)+1]; fi;
      for p in orbit do
        i := p[1]+offset[1]; j := p[2]+offset[2];
        if i in [1..height] and j in [1..width] then grid[i][j] := z; fi;
      od;
      SaveAsBitmapPicture(grid,filename);
    fi;
    if ValueOption("ReturnPicture") = true then return grid; fi;
  end );

#############################################################################
##
#S  Computing elements which map a given tuple to a given other tuple. //////
##
#############################################################################

#############################################################################
##
#M  RepresentativesActionPreImage( <G>, <src>, <dest>, <act>, <F> )
##
InstallMethod( RepresentativesActionPreImage,
               "for rcwa groups and permutation groups (RCWA)", ReturnTrue,
               [ IsFinitelyGeneratedGroup, IsObject, IsObject,
                 IsFunction, IsFreeGroup ], 0,

  function ( G, src, dest, act, F )

    local  SetOfRepresentatives, Extended, R, gensG, gensF, 
           orbsrc, orbdest, orbitlengthbound, g, extstep, oldorbsizes,
           inter, intersrc, interdest, compatible, quots, pointlimit, onlyup;

    SetOfRepresentatives := function ( S, rel, less, pref )

      local  reps, elm, pos;

      if IsEmpty(S) then return []; fi;
      Sort(S,less); reps := [S[1]]; pos := 1;
      for elm in S do
        if rel(elm,reps[pos]) then
          if pref(elm,reps[pos]) then reps[pos] := elm; fi;
        else
          pos := pos + 1;
          reps[pos] := elm;
        fi;
      od;
      return reps;
    end;

    Extended := function ( orb, gens )

      local  eq, lt, shorter, nextlayer, g, i;

      nextlayer := List(gens,g->List(orb,t->[act(t[1],g),
                                     t[2]*gensF[Position(gensG,g)]]));
      if IsRcwaGroupOverZ(G) and pointlimit < infinity then
        for i in [1..Length(nextlayer)] do
          nextlayer[i] := Filtered(nextlayer[i],
                                   entry -> Maximum(entry[1]) <= pointlimit);
        od;
      fi;
      orb := Union(Concatenation([orb],nextlayer));
      eq := function(t1,t2) return t1[1] = t2[1]; end;
      lt := function(t1,t2) return t1[1] < t2[1]; end;
      shorter := function(t1,t2) return Length(t1[2]) < Length(t2[2]); end;
      return SetOfRepresentatives(orb,eq,lt,shorter);
    end;

    orbitlengthbound := GetOption("OrbitLengthBound",infinity,IsPosInt);
    pointlimit := GetOption("pointlimit",infinity,IsPosInt);
    onlyup := ValueOption("onlyup") = true;

    if   IsPermGroup(G)
    then R := PositiveIntegers;
    elif IsRcwaGroup(G) then R := Source(One(G));
    else TryNextMethod(); fi;
    if not IsList(src)  then src  := [src];  fi;
    if not IsList(dest) then dest := [dest]; fi;
    if   not IsSubset(R,Union(src,dest))
      or Length(GeneratorsOfGroup(G)) <> Length(GeneratorsOfGroup(F))
    then TryNextMethod(); fi;
    if Length(src) <> Length(dest) then return []; fi;
    if IsRcwaGroup(G) then
      if not IsSubset(dest,Difference(src,Support(G))) then return []; fi;
      if not IsSubset(src,Difference(dest,Support(G))) then return []; fi;
    fi;
    gensF := GeneratorsAndInverses(F); gensG := GeneratorsAndInverses(G);
    orbsrc := [[src,One(F)]]; orbdest := [[dest,One(F)]]; extstep := 0;
    repeat
      oldorbsizes := [Length(orbsrc),Length(orbdest)];
      if Maximum(oldorbsizes) > orbitlengthbound then
        Info(InfoRCWA,2,"Orbit lengths exceeded user-specified bound.");
        return [];
      fi;
      orbsrc := Extended(orbsrc,gensG); orbdest := Extended(orbdest,gensG);
      if onlyup then
        orbdest := Filtered(orbdest,pt->Maximum(pt[1])>=Maximum(src)
                                     or Maximum(pt[1]) =Maximum(dest));
      fi;
      extstep := extstep + 1;
      Info(InfoRCWA,2,"Orbit lengths after extension step ",extstep,": ",
                      [Length(orbsrc),Length(orbdest)]);
      if Maximum(Length(orbsrc),Length(orbdest)) < 100 then
        Info(InfoRCWA,3,"Orbits after extension step ",extstep,":\n",
                        orbsrc,"\n",orbdest);
      fi;
      inter := Intersection(List(orbsrc,t->t[1]),List(orbdest,t->t[1]));
    until inter <> [] or oldorbsizes = [Length(orbsrc),Length(orbdest)];
    if inter = [] then return []; fi;
    intersrc   := Filtered(orbsrc, t->t[1] in inter);
    interdest  := Filtered(orbdest,t->t[1] in inter);
    compatible := Filtered(Cartesian(intersrc,interdest),t->t[1][1]=t[2][1]);
    Info(InfoRCWA,2,"|Candidates| = ",Length(compatible));
    quots := Set(compatible,t->t[1][2]*t[2][2]^-1);
    if IsRcwaGroupOverZ(G) then
      quots := List(quots,quot->ReducedWordByOrdersOfGenerators(quot,
                                  List(GeneratorsOfGroup(G),Order)));
    fi;
    return Set(quots);
  end );

#############################################################################
##
#M  RepresentativeActionPreImage( <G>, <src>, <dest>, <act>, <F> )
##
InstallMethod( RepresentativeActionPreImage,
               "for rcwa groups and permutation groups (RCWA)", ReturnTrue,
               [ IsGroup, IsObject, IsObject, IsFunction, IsFreeGroup ],
               0,

  function ( G, src, dest, act, F )

    local  candidates, minlng, shortest;

    if not IsRcwaGroup(G) and not IsPermGroup(G) then TryNextMethod(); fi;
    candidates := RepresentativesActionPreImage(G,src,dest,act,F);
    if candidates = [] then return fail; fi;
    minlng   := Minimum(List(candidates,Length));
    shortest := Filtered(candidates,cand->Length(cand)=minlng)[1];
    return shortest;
  end );

#############################################################################
##
#M  RepresentativeActionOp( <G>, <src>, <dest>, <act> )
##
InstallMethod( RepresentativeActionOp,
               "for rcwa groups (RCWA)", ReturnTrue,
               [ IsRcwaGroup and IsFinitelyGeneratedGroup,
                 IsObject, IsObject, IsFunction ], 0,

  function ( G, src, dest, act )

    local  F, phi, pre;

     phi := EpimorphismFromFreeGroup(G);
     pre := RepresentativeActionPreImage(G,src,dest,act,Source(phi));
     if pre = fail then return fail; fi;
     return pre^phi;
  end );

#############################################################################
##
#S  Factoring elements of an rcwa group into the generators. ////////////////
##
#############################################################################

#############################################################################
##
#M  EpimorphismFromFreeGroup( <G> ) . . . . . . . . . . . . . for rcwa groups
##
InstallMethod( EpimorphismFromFreeGroup,
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )

    local  F, gens, gensnames, i;

    gens := GeneratorsOfGroup(G); gensnames := [];
    for i in [1..Length(gens)] do
      if   HasName(gens[i])
      then Add(gensnames,Name(gens[i]));
      else Add(gensnames,Concatenation("f",String(i))); fi;
    od;
    F := FreeGroup(gensnames);
    return GroupHomomorphismByImagesNC(F,G);
  end );

#############################################################################
##
#M  PreImagesRepresentatives( <phi>, <g> )
##
InstallMethod( PreImagesRepresentatives,
               "for hom's from free groups to rcwa groups (RCWA)",
               ReturnTrue, [ IsGroupHomomorphism, IsObject ], 0,

  function ( phi, g )

    local  IsCorrect, F, G, R, q, x, candidates, minlng, shortest,
           preimage, image, lng, add;

    IsCorrect := function ( cand )

      local  gens, letters, factors, testrange, f, n, m;

      gens      := GeneratorsOfGroup(G);
      letters   := LetterRepAssocWord(cand);
      factors   := List(letters,i->gens[AbsInt(i)]^SignInt(i));
      testrange := [1..3*Length(letters)+1];
      if   IsRcwaGroupOverGFqx(G)
      then testrange := AllResidues(R,x^(LogInt(Length(testrange),q)+1)); fi;
      for n in testrange do
        m := n; for f in factors do m := m^f; od;
        if m <> n^g then return false; fi;
      od;
      return cand^phi = g;
    end;

    F := Source(phi); G := Range(phi);
    if   not IsFreeGroup(F)
      or not (   IsRcwaGroupOverZOrZ_pi(G) or IsRcwaGroupOverGFqx(G)
              or IsPermGroup(G))
      or FamilyObj(g) <> FamilyObj(One(G))
      or MappingGeneratorsImages(phi) <> List([F,G],GeneratorsOfGroup)
    then TryNextMethod(); fi;
    lng := 1; add := 1;
    repeat
      lng := lng + add; add := add + 1; 
      # formerly: if IsPermGroup(G) then add := add + 1; fi;
      preimage := [1..lng];
      if IsRcwaGroupOverGFqx(G) then
        R        := Source(One(G));
        q        := Size(CoefficientsRing(R));
        x        := IndeterminatesOfPolynomialRing(R)[1];
        preimage := AllResidues(R,x^(LogInt(lng,q)+1)){preimage};
      fi;
      image      := List(preimage,n->n^g);
      candidates := RepresentativesActionPreImage(G,preimage,image,
                                                  OnTuples,F);
      if candidates = [] then return []; fi;
      candidates := Filtered(candidates,IsCorrect);
    until candidates <> [];
    return candidates;
  end );

#############################################################################
##
#M  PreImagesRepresentativeNC( <phi>, <g> )
##
InstallMethod( PreImagesRepresentativeNC,
               "for hom's from free groups to rcwa- or perm.-groups (RCWA)",
               ReturnTrue, [ IsGroupHomomorphism, IsObject ], SUM_FLAGS,

  function ( phi, g )

    local  candidates, minlng, shortest;

    if not IsRcwaMapping(g) and
       not (IsPerm(g) and ValueOption("NoStabChain") = true)
    then TryNextMethod(); fi;
    candidates := PreImagesRepresentatives(phi,g);
    if candidates = [] then return fail; fi;
    minlng   := Minimum(List(candidates,Length));
    shortest := Filtered(candidates,cand->Length(cand)=minlng)[1];
    return shortest;
  end );

#############################################################################
##
#M  Factorization( <G>, <g> ) . . . . . . .  for rcwa mappings in rcwa groups
##
InstallMethod( Factorization,
               "for rcwa mappings in rcwa groups (RCWA)", true,
               [ IsRcwaGroup, IsRcwaMapping ], 0,

  function ( G, g )
    return PreImagesRepresentativeNC(EpimorphismFromFreeGroup(G),g);
  end );

#############################################################################
##
#S  Methods for `AbelianInvariants', `IsSolvableGroup', `IsPerfectGroup',
#S `IsSimpleGroup' and `Exponent'.
##
#############################################################################

#############################################################################
##
#M  AbelianInvariants( <G> ) . .  for groups knowing an iso. to a pcp group
#M  AbelianInvariants( <G> ) . .  for groups knowing an iso. to a perm.-group
##
InstallMethod( AbelianInvariants,
               "for groups knowing an isomorphism to a pcp group", true,
               [ IsGroup and HasIsomorphismPcpGroup ], 0,
  function ( G )
    if IsPcpGroup(G) then TryNextMethod(); fi; # avoid recursion
    return AbelianInvariants(Image(IsomorphismPcpGroup(G)));
  end );

InstallMethod( AbelianInvariants,
               "for groups knowing an isomorphism to a permutation group",
               true, [ IsGroup and HasIsomorphismPermGroup ], 0,
  function ( G )
    if IsPermGroup(G) then TryNextMethod(); fi; # avoid recursion
    return AbelianInvariants(Image(IsomorphismPermGroup(G)));
  end );

#############################################################################
##
#M  IsSolvableGroup( <G> ) . . . . . . . . . . . . . . . . .  for rcwa groups
##
InstallMethod( IsSolvableGroup,
               "for rcwa groups (RCWA)", ReturnTrue, [ IsRcwaGroup ], 0,

  function ( G )
    if IsAbelian(G) then return true; fi;
    if not IsTame(G) then TryNextMethod(); fi;
    return IsSolvableGroup(ActionOnRespectedPartition(G));
  end );

#############################################################################
##
#M  IsPerfectGroup( <G> ) . . . . . . . . . . . . . . . . . . for rcwa groups
##
InstallMethod( IsPerfectGroup,
               "for rcwa groups (RCWA)", ReturnTrue, [ IsRcwaGroup ], 0,

  function ( G )

    local  H, orbs, quots;

    if IsTrivial(G) then return true; fi;
    if IsAbelian(G) then return false; fi;

    if IsRcwaGroupOverZ(G) then
      if   ForAny(GeneratorsOfGroup(G),g->Sign(g)<>1)
        or (     IsClassWiseOrderPreserving(G)
             and ForAny(GeneratorsOfGroup(G),g->Determinant(g)<>0))
      then return false; fi;
    fi;

    orbs  := ShortOrbits(G,AllResidues(Source(One(G)),
                                       Lcm(List(GeneratorsOfGroup(G),
                                                Modulus))),64);
    quots := List(orbs,orb->Action(G,orb));
    if not ForAll(quots,IsPerfectGroup) then return false; fi;

    if IsFinite(G) then return IsPerfectGroup(Image(IsomorphismPermGroup(G))); fi;

    if not IsTame(G) then TryNextMethod(); fi;

    H := ActionOnRespectedPartition(G);
    if   IsTransitive(H,[1..LargestMovedPoint(H)])
    then return IsPerfectGroup(H); else TryNextMethod(); fi;
  end );

#############################################################################
##
#M  IsSimpleGroup( <G> ) . . . . . . . . . . . . . . . . . .  for rcwa groups
##
InstallMethod( IsSimpleGroup,
               "for rcwa groups (RCWA)", ReturnTrue, [ IsRcwaGroup ], 0,

  function ( G )

    local  interval;

    if   IsRcwaGroupOverZ(G) then
      interval := [-Lcm(List(GeneratorsOfGroup(G),Modulus))..
                       Lcm(List(GeneratorsOfGroup(G),Modulus))];
    else
      interval := AllResidues(Source(One(G)),
                              Lcm(List(GeneratorsOfGroup(G),
                                       Modulus)));
    fi;
    interval := Intersection(interval,Support(G));

    if   ShortOrbits(G,interval,64) <> [] or not IsPerfectGroup(G)
    then return false; fi;

    if IsTame(G) then
      if   IsFinite(G)
      then return IsSimpleGroup(Image(IsomorphismPermGroup(G)));
      else return false; fi;
    fi;

    TryNextMethod();
  end );

#############################################################################
##
#M  Exponent( <G> ) . . . . . . . . . . . . . . . . . . . . . for rcwa groups
##
InstallMethod( Exponent,
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )

    local  k;

    if IsNaturalRCWA_OR_CT(G) then return infinity; fi;
    if IsFinite(G) then return Exponent(Image(IsomorphismPermGroup(G))); fi;
    if IsTame(G)   then return infinity; fi;
    if   ForAny(GeneratorsOfGroup(G),g->Order(g)=infinity)
    then return infinity; fi;
    if First(G,g->Order(g)=infinity) <> fail then return infinity; fi;
  end );

#############################################################################
##
#S  Subgroup indices and natural homomorphisms. /////////////////////////////
##
#############################################################################

#############################################################################
##
#M  IndexNC( <G>, <H> ) . . . . . . . . . . . . . . . . . . . for rcwa groups
##
InstallMethod( IndexNC,
               "for rcwa groups (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsRcwaGroup ], 0,

  function ( G, H )
    if IsFinite(G) then return Size(G)/Size(H); fi;
    if IsFinite(H) and not IsFinite(G) then return infinity; fi;
    if HasIsSimpleGroup(G) and IsSimpleGroup(G)
      and not IsFinite(G) and H <> G
    then return infinity; fi;
    if H = G then return 1; fi;
    return Length(RightCosets(G,H));
  end );

#############################################################################
##
#M  IndexNC( <G>, <H> ) . . . . . . . . . . . . . . .  for rcwa groups over Z
##
InstallMethod( IndexNC,
               "for rcwa groups over Z (RCWA)", ReturnTrue,
               [ IsRcwaGroupOverZ, IsRcwaGroupOverZ ], 0,

  function ( G, H )
    if HasGeneratorsOfGroup(G) and IsClassWiseOrderPreserving(G)
      and Set(List(GeneratorsOfGroup(G),Determinant)) <> [0]
      and Set(List(GeneratorsOfGroup(H),Determinant)) =  [0]
    then return infinity; fi;
    if IsFinite(G) then return Size(G)/Size(H); fi;
    if IsTame(H) and not IsTame(G) then return infinity; fi;
    if IsTame(G) and RankOfKernelOfActionOnRespectedPartition(G)
                   > RankOfKernelOfActionOnRespectedPartition(H)
    then return infinity; fi;
    if HasIsSimpleGroup(G) and IsSimpleGroup(G)
      and not IsFinite(G) and H <> G
    then return infinity; fi;
    if H = G then return 1; fi;
    return Length(RightCosets(G,H));
  end );

#############################################################################
##
#M  NaturalHomomorphismByNormalSubgroupNCOrig( <G>, <N> ) . . for rcwa groups
##
InstallMethod( NaturalHomomorphismByNormalSubgroupNCOrig,
               "for rcwa groups, requiring finite index (RCWA)", ReturnTrue,
               [ IsRcwaGroup, IsRcwaGroup ], 0,

  function ( G, N )

    local  cosets, img;

    if not IsNormal(G,N) then return fail; fi;
    if IndexNC(G,N) = infinity then TryNextMethod(); fi;
    cosets := RightCosets(G,N);
    img    := Action(G,cosets,OnRight);
    return GroupHomomorphismByImagesNC(G,img);
  end );

#############################################################################
##
#S  Computing normal forms of words and relators. ///////////////////////////
##
#############################################################################

#############################################################################
##
#F  ReducedWordByOrdersOfGenerators( <w>, <gensords> )
##
InstallGlobalFunction(  ReducedWordByOrdersOfGenerators,

  function ( w, gensords )

    local  ext, fam, i;

    fam := FamilyObj(w);
    ext := ShallowCopy(ExtRepOfObj(w));
    for i in [1,3..Length(ext)-1] do
      if gensords[ext[i]] < infinity then
        ext[i+1] := ext[i+1] mod gensords[ext[i]];
        if   ext[i+1] > gensords[ext[i]]/2
        then ext[i+1] := ext[i+1] - gensords[ext[i]]; fi;
      fi;
    od;
    return ObjByExtRep(fam,ext);
  end );

#############################################################################
##
#M  NormalizedRelator( <w>, <gensords> )
##
InstallMethod( NormalizedRelator,
               "for a word and a list of orders of generators", ReturnTrue,
               [ IsAssocWord, IsList ], 0,

  function ( w, gensords )

    local  c, old, twice, words, min, max, start, i, j;

    c := ShallowCopy(ExtRepOfObj(w));
    if Length(c) = 2 then
      if c[2] mod gensords[c[1]] = 0 then
        return ObjByExtRep(FamilyObj(w),[c[1],gensords[c[1]]]);
      else
        return ObjByExtRep(FamilyObj(w),[c[1],c[2] mod gensords[c[1]]]);
      fi;
    fi;
    repeat
      old := ShallowCopy(c);
      for i in [2,4..Length(c)] do
        if   gensords[c[i-1]] < infinity
        then c[i] := c[i] mod gensords[c[i-1]]; fi;
      od;
      c := ShallowCopy(ExtRepOfObj(ObjByExtRep(FamilyObj(w),c)));
      if c = [] then return One(w); fi;
      min   := Minimum(c{[1,3..Length(c)-1]});
      start := Filtered([1,3..Length(c)-1],i->c[i]=min);
      max   := Maximum(c{start+1});
      start := Filtered(start,i->c[i+1]=max);
      twice := Concatenation(c,c);
      words := List(start,i->twice{[i..i+Length(c)-1]});
      SortParallel(List(words,v->[v{[1,3..Length(v)-1]},
                                  v{[2,4..Length(v)]}]),words);
      c := words[1];
    until c = old;
    w := ObjByExtRep(FamilyObj(w),c);
    return w;
  end );

#############################################################################
##
#S  Rcwa groups as epimorphic images of finitely presented groups. //////////
##
#############################################################################

#############################################################################
##
#M  EpimorphismFromFpGroup( <G>, <r> ) . . . . default method for f.g. groups
##
InstallMethod( EpimorphismFromFpGroup,
               "default method (RCWA)", ReturnTrue,
               [ IsFinitelyGeneratedGroup, IsPosInt ], 0,

  function ( G, r )

    local  Fp, FpS, phi, phiFp, phiFpS, F, BF, BG, BGset, rels, gensF, g, w,
           ShortGroupRelations;

    if IsFpGroup(G) then return IdentityMapping(G); fi;

    phi       := EpimorphismFromFreeGroup(G);
    F         := Source(phi);
    gensF     := GeneratorsOfGroup(F);

    if IsReadOnlyGlobal( "ShortGroupRelations" ) then # FR is loaded.

      ShortGroupRelations := ValueGlobal( "ShortGroupRelations" );

      rels := ShortGroupRelations(G,r);
      rels := rels{[2..Length(rels)]};

    else

      BF        := Ball(F,One(F),r);
      BG        := List(BF,g->Image(phi,g));
      BGset     := Set(BG);

      rels      := [];
      for g in BGset do
        Append(rels,List(Combinations(BF{Positions(BG,g)},2),w->w[1]/w[2]));
        if Order(g) < infinity then
          w := BF[Position(BG,g)];
          if Maximum(ExponentSums(w)) >= 0 then Add(rels,w^Order(g)); fi;
        fi;
      od;
      rels := Difference(rels,[One(F)]);

    fi;

    Fp     := F/rels;
    phiFp  := GroupHomomorphismByImagesNC(Fp,G);
    phiFpS := InverseGeneralMapping(IsomorphismSimplifiedFpGroup(Fp));

    return CompositionMapping(phiFp,phiFpS);
  end );

#############################################################################
##
#M  EpimorphismFromFpGroup( <G>, <r> ) . . . . . . . . . . .  for rcwa groups
##
InstallMethod( EpimorphismFromFpGroup,
               "for rcwa groups (RCWA)", ReturnTrue,
               [ IsRcwaGroup and IsFinitelyGeneratedGroup, IsPosInt ], 10,

  function ( G, r )

    local  gensG, gensF, F, Q, R, P, rels, B, W, phi, n, letters, orders,
           gens1, gens2, g, h, m, v, w, i, j, k, pos, timeout, starttime;

    timeout     := ValueOption("timeout");
    starttime   := Runtime();

    letters := ["a","b","c","d","e","f","g","h","k","l","m","n"];
    gensG   := GeneratorsOfGroup(G);
    orders  := List(gensG,Order);
    n       := Length(gensG);
    if n <= 12 then F := FreeGroup(letters{[1..n]});
               else F := FreeGroup(n); fi;
    gensF   := GeneratorsOfGroup(F);

    Info(InfoRCWA,1,"EpimorphismFromFpGroup: computing ball of radius ",r);

    B := Ball(G,One(G),r:Spheres);
    W := [[One(F)]];
    for i in [2..r+1] do
      W[i] := [];
      for j in [1..Length(B[i-1])] do
        g := B[i-1][j];
        v := W[i-1][j];
        for k in [1..n] do
          h := g*gensG[k];
          w := v*gensF[k];
          pos := Position(B[i],h);
          if pos <> fail then
            W[i][pos] := w;
          fi;
          if Order(gensG[k]) > 2 then
            h := g*gensG[k]^-1;
            w := v*gensF[k]^-1;
            pos := Position(B[i],h);
            if pos <> fail then
              W[i][pos] := w;
            fi;
          fi;
        od;
      od;
    od;

    Info(InfoRCWA,1,"EpimorphismFromFpGroup: searching for relations");

    rels   := [];
    for i in [2..r+1] do
      if IsInt(timeout) and Runtime()-starttime >= timeout
      then break; fi;
      Info(InfoRCWA,1,"r = ",i-1);
      for j in [1..Length(B[i])] do
        if IsInt(timeout) and Runtime()-starttime >= timeout
        then break; fi;
        Info(InfoRCWA,2,"Element number = ",j);
        g := B[i][j];
        w := W[i][j];
        m := Order(g);
        if IsInfinity(m) then continue; fi;
        Add(rels,w^m);
      od;
    od;
    rels := Set(rels,w->NormalizedRelator(w,orders));
    rels := Filtered(rels,w->not IsOne(w));

    Q := F/rels;
    P := PresentationFpGroup(Q); gens1 := GeneratorsOfPresentation(P);
    TzGoGo(P);                   gens2 := GeneratorsOfPresentation(P);
    if gens1 = gens2 then
      R := FpGroupPresentation(P);
      rels := Set(RelatorsOfFpGroup(R),w->NormalizedRelator(w,orders));
      rels := List(rels,w->ObjByExtRep(FamilyObj(gens2[1]),ExtRepOfObj(w)));
      R := F/List(rels,w->ObjByExtRep(FamilyObj(One(F)),ExtRepOfObj(w)));
      if Sum(RelatorsOfFpGroup(R),Length) < Sum(RelatorsOfFpGroup(Q),Length)
        and ForAll(rels,w->IsOne(MappedWord(w,gens2,gensG)))
      then
        Q := R;
        Info(InfoRCWA,1,"After Tietze transformations: ",
                        Length(RelatorsOfFpGroup(Q)),
                        " relations of total length ",
                        Sum(List(RelatorsOfFpGroup(Q),Length)),".");
      else
        Info(InfoRCWA,1,"Tietze transformations unsuccessful.");
      fi;
    fi;

    phi := GroupHomomorphismByImagesNC(Q,G);
    return phi;
  end );

#############################################################################
##
#M  EpimorphismFromFpGroup( <G>, <r>, <maxparts> ) . . . . .  for rcwa groups
##
InstallMethod( EpimorphismFromFpGroup,
               "for rcwa groups, bound on number of affine parts (RCWA)",
               ReturnTrue, [ IsRcwaGroup and IsFinitelyGeneratedGroup,
                             IsPosInt, IsPosInt ], 0,

  function ( G, r_max, maxparts )

    local  process_entry, basic_reduction,
           gensG, gensF, F, Q, R, phi, inverseclosed, inversepos,
           B, S_last, S, S_next, hashtable, hashsize, hash, r,
           rels, relslast, pairs, pair, n, letters, orders, P, gens1, gens2,
           g, h, w, v, i, j, lng, inv, pos, last_j, powlast_j;

    process_entry := function ( )

      local  inds;

      if Length(Coefficients(h)) <= maxparts then
        hash := HashValueOfRcwaMapping(h,hashsize);
        pairs := [];
        for inds in hashtable[hash] do
          if inds[1] <= Length(B) then
            if   B[inds[1]][inds[2]][1] = h
            then Add(pairs,B[inds[1]][inds[2]]); fi;
          elif inds[1] = Length(B) + 1 then
            if   S_next[inds[2]][1] = h
            then Add(pairs,S_next[inds[2]]); fi;
          fi;
        od;
        for pair in pairs do
          Add(rels,v/pair[2]);
        od;
        if pairs = [] then
          if   j <> last_j
          then Add(S_next,[h,v,j,1]);
          else Add(S_next,[h,v,j,powlast_j+1]); fi;
          Add(hashtable[hash],[r+1,Length(S_next)]);
        fi;
      fi;
    end;

    basic_reduction := function ( )

      local  stepnr;

      if ValueOption("OnlyTietze") = true then
        rels := Set(rels,w->NormalizedRelator(w,orders));
        rels := Filtered(rels,w->not IsOne(w));
      fi;
      stepnr := 1;
      if rels <> [] then
        repeat
          Info(InfoRCWA,1,"Reduction step ",stepnr," (",Length(rels),":",
                          Sum(List(rels,Length)),")");
          relslast := rels;
          rels     := relslast{[1]};
          for i in [2..Length(relslast)] do
            w := relslast[i];
            for j in [1..i-1] do
              while PositionWord(w,relslast[j]) <> fail do
                pos := PositionWord(w,relslast[j]);
                w := SubstitutedWord(w,pos,pos+Length(relslast[j])-1,One(w));
              od;
              inv := NormalizedRelator(relslast[j]^-1,orders);
              while PositionWord(w,inv) <> fail do
                pos := PositionWord(w,inv);
                w := SubstitutedWord(w,pos,pos+Length(inv)-1,One(w));
              od;
            od;
            if not IsOne(w) then Add(rels,w); fi;
          od;
          rels := Set(rels,w->NormalizedRelator(w,orders));
          stepnr := stepnr + 1;
        until rels = relslast;
        rels := Set(rels,w->NormalizedRelator(w,orders));
      fi;
    end;

    G             := SparseRep(G);
    gensG         := GeneratorsOfGroup(G);
    if not IsDuplicateFreeList(gensG) then TryNextMethod(); fi;
    n             := Length(gensG);
    letters       := ["a","b","c","d","e","f","g","h","k","l","m","n"];
    if n <= 12 then F := FreeGroup(letters{[1..n]});
               else F := FreeGroup(n); fi;
    gensF         := GeneratorsOfGroup(F);
    orders        := List(gensG,Order);
    inverseclosed := ForAll(gensG,g->g^-1 in gensG);
    inversepos    := List(gensG,g->Position(gensG,g^-1));
    hashsize      := GetOption("hashsize",10000,IsPosInt);

    Info(InfoRCWA,1,"EpimorphismFromFpGroup for G = ",ViewString(G));
    hashtable := List([1..hashsize],n->[]);
    S_last := []; S := [[One(G),One(F),0,0]]; B := [S]; rels := [];
    for r in [1..r_max] do
      Info(InfoRCWA,1,"r = ",r-1,": |S| = ",Length(S),
                      ", |rels| = ",Length(rels));
      if S = [] then break; fi;
      S_next := [];
      for i in [1..Length(S)] do
        g := S[i][1]; w := S[i][2]; last_j := S[i][3]; powlast_j := S[i][4];
        for j in [1..n] do
          if j <> last_j or powlast_j + 1 < orders[j] then
            if last_j <> 0 and j = inversepos[last_j] then continue; fi;
            h := g*gensG[j]; v := w*gensF[j];
            process_entry();
            if orders[j] = infinity and inversepos[j] = fail then
              h := g*gensG[j]^-1; v := w*gensF[j]^-1;
              process_entry();
            fi;
          fi;
        od;
      od;
      S_last := S;
      S := S_next;
      Add(B,S);
    od;
    Info(InfoRCWA,1,"Found ",Length(rels)," relations of total length ",
                    Sum(List(rels,Length)),".");

    if ValueOption("OnlyTietze") <> true then
      rels := Set(rels,w->NormalizedRelator(w,orders));
      rels := Filtered(rels,w->not IsOne(w));
      Info(InfoRCWA,1,"After first reduction: ",Length(rels),
                      " relations of total length ",
                      Sum(List(rels,Length)),".");
    fi;

    Info(InfoRCWA,1,"Trying Tietze transformations ...");

    # Try to simplify the presentation by Tietze transformations;
    # take the outcome if successful and if no change of generators occurs.

    rels := Union(List(Difference([1..n],Positions(orders,infinity)),
                       i->gensF[i]^orders[i]),rels);
    Q := F/rels;
    P := PresentationFpGroup(Q); gens1 := GeneratorsOfPresentation(P);
    TzGoGo(P);                   gens2 := GeneratorsOfPresentation(P);
    if gens1 = gens2 then
      R := FpGroupPresentation(P);
      rels := Set(RelatorsOfFpGroup(R),w->NormalizedRelator(w,orders));
      rels := List(rels,w->ObjByExtRep(FamilyObj(gens2[1]),ExtRepOfObj(w)));
      R := F/List(rels,w->ObjByExtRep(FamilyObj(One(F)),ExtRepOfObj(w)));
      if Sum(RelatorsOfFpGroup(R),Length) < Sum(RelatorsOfFpGroup(Q),Length)
        and ForAll(rels,w->IsOne(MappedWord(w,gens2,gensG)))
      then
        Q := R;
        Info(InfoRCWA,1,"After Tietze transformations: ",
                        Length(RelatorsOfFpGroup(Q)),
                        " relations of total length ",
                        Sum(List(RelatorsOfFpGroup(Q),Length)),".");
      else
        Info(InfoRCWA,1,"Tietze transformations unsuccessful, ",
                        "trying basic reductions ... ");
        rels := RelatorsOfFpGroup(Q);
        basic_reduction();
        Q := F/rels;
      fi;
    else
      Info(InfoRCWA,1,"Tietze transformations changed generating set, ",
                      "trying basic reductions ... ");
      rels := RelatorsOfFpGroup(Q);
      basic_reduction();
      Q := F/rels;
    fi;

    phi := GroupHomomorphismByImagesNC(Q,G);
    return phi;
  end );

#############################################################################
##
#S  Finite quotients of finitely presented groups. //////////////////////////
##
#############################################################################

#############################################################################
##
#M  EpimorphismsUpToAutomorphisms( G, H )  for an fp group and a finite group
##
InstallMethod( EpimorphismsUpToAutomorphisms,
               "for an fp group and a finite group (RCWA)", ReturnTrue,
               [ IsFpGroup, IsGroup and IsFinite ], 0,

  function ( G, H )

    local  search, epis, rels, gensG, gensF, A, orders, r, l;

    search := function ( imgs, stab )

      local  reps, rep, stab_new;

      if Length(imgs) = Length(gensG) then
        if H = Group(imgs)
          and ForAll(rels,r->IsOne(MappedWord(r,gensF,imgs)))
        then
          Add(epis,GroupHomomorphismByImagesNC(G,H,gensG,imgs));
        fi;
      else
        reps := List(OrbitsDomain(stab,H),Representative);
        if orders[Length(imgs)+1] <> fail then
          reps := Filtered(reps,g->orders[Length(imgs)+1] mod Order(g) = 0);
        fi;
        for rep in reps do
          if Length(imgs) < Length(gensG) - 1 then
            stab_new := Intersection(stab,Stabilizer(A,rep));
          else # not needed any more
            stab_new := stab;
          fi;
          search(Concatenation(imgs,[rep]),stab_new);
        od;
      fi;
    end;

    gensG  := GeneratorsOfGroup(G);
    gensF  := GeneratorsOfGroup(FreeGroupOfFpGroup(G));
    orders := List(gensG,g->fail);
    rels   := RelatorsOfFpGroup(G);
    A      := AutomorphismGroup(H);

    for r in rels do
      l := ExtRepOfObj(r);
      if Length(l) = 2 then
        orders[l[1]] := l[2];
      fi;
    od;

    epis   := [];
    search([],A);

    return epis;
  end );

#############################################################################
##
#S  Computing structure descriptions for rcwa groups. ///////////////////////
##
#############################################################################

#############################################################################
##
#M  DirectFactorsOfGroup( <G> ) . . . . . . .  for a finite subgroup of CT(Z)
##
InstallMethod( DirectFactorsOfGroup,
               "for a finite subgroup of CT(Z) (RCWA)", true,
               [ IsRcwaGroupOverZ ], 0,

  function ( G )

    local  P, H, facts_H, Hi, facts_G, Gi;

    if not IsSignPreserving(G) or not IsTame(G) then TryNextMethod(); fi;
    P := RespectedPartition(G);
    H := Action(G,P);
    facts_H := DirectFactorsOfGroup(H);
    facts_G := [];
    for Hi in facts_H do
      Gi := Group(List(GeneratorsOfGroup(Hi),
                       h->RcwaMapping(Permuted(P,h),P)));
      Add(facts_G,Gi);
    od;
    return facts_G;
  end );

#############################################################################
##
#M  StructureDescription( <G> ). . . . . . . . . . . . for rcwa groups over Z
##
InstallMethod( StructureDescription,
               "for rcwa groups over Z (RCWA)",
               true, [ IsRcwaGroupOverZ ],
               # the following rank adjustments works around an issue where a
               # generic StructureDescription for abelian groups in GAP is
               # triggered, which does not actually support infinite groups
               NICE_FLAGS,

  function ( G )

    local  desc, short, P, H, rank, top, bottom, ords, factors, descs, gens,
           bound, domain, induced, commgraph, e, comps, comp, comp_old, rem,
           i;

    if not IsFinitelyGeneratedGroup(G) then
      TryNextMethod();
    fi;
    short := ValueOption("short") <> fail;

    if IsTrivial(G) then
      return "1";
    fi;
    if IsFinite(G) then
      return StructureDescription(Image(IsomorphismPermGroup(G)));
    fi;
    if HasDirectProductInfo(G) then
      factors := DirectProductInfo(G)!.groups;
      descs   := Filtered(List(factors,StructureDescription),str->str<>"1");
      for i in [1..Length(descs)] do
        if   Intersection(descs[i],"x:.*wr") <> ""
        then descs[i] := Concatenation("(",descs[i],")"); fi; 
      od;
      desc := descs[1];
      for i in [2..Length(descs)] do
        desc := Concatenation(desc," x ",descs[i]);
      od;
      if short then RemoveCharacters(desc," "); fi;
      return desc;
    fi;
    if HasWreathProductInfo(G) then
      factors := WreathProductInfo(G)!.groups;
      descs   := List(factors,StructureDescription);
      for i in [1..2] do
        if   Intersection(descs[i],"x:.*wr") <> ""
        then descs[i] := Concatenation("(",descs[i],")"); fi; 
      od;
      desc := Concatenation(descs[1]," wr ",descs[2]);
      if short then RemoveCharacters(desc," "); fi;
      return desc;
    fi;
    if HasFreeProductInfo(G) then
      factors := FreeProductInfo(G)!.groups;
      descs   := Filtered(List(factors,StructureDescription),str->str<>"1");
      for i in [1..Length(descs)] do
        if   Intersection(descs[i],"x:.*wr") <> ""
        then descs[i] := Concatenation("(",descs[i],")"); fi; 
      od;
      desc := descs[1];
      for i in [2..Length(descs)] do
        desc := Concatenation(desc," * ",descs[i]);
      od;
      if short then RemoveCharacters(desc," "); fi;
      return desc;
    fi;
    gens := DuplicateFreeList(Filtered(GeneratorsOfGroup(G),
                                       gen->not IsOne(gen)));
    List(gens,IsTame);
    if   Length(gens) = 2 and not IsAbelian(G) and List(gens,Order) = [2,2]
    then if not IsTame(Product(gens)) or Order(Product(gens)) = infinity
         then return "D0"; fi; fi;
    if Length(gens) = 2 and Set(List(gens,Order)) = [2,infinity] then
      if   Order(gens[1]) = infinity and gens[1]^gens[2] = gens[1]^-1
        or Order(gens[2]) = infinity and gens[2]^gens[1] = gens[2]^-1
      then return "D0"; fi;
    fi;
    commgraph := Filtered(Combinations([1..Length(gens)],2),
                          e->not IsEmpty(Intersection(Support(gens[e[1]]),
                                                      Support(gens[e[2]]))));
    comps := []; rem := [1..Length(gens)];
    repeat
      comp := [rem[1]];
      repeat
        comp_old := ShallowCopy(comp);
        for e in commgraph do
          if Intersection(comp,e) <> [] then comp := Union(comp,e); fi;
        od;
      until comp = comp_old;
      Add(comps,comp);
      rem := Difference(rem,comp);
    until rem = [];
    if Length(comps) > 1 then
      descs := List(comps,comp->StructureDescription(Group(gens{comp})));
      desc  := ShallowCopy(descs[1]);
      for i in [2..Length(comps)] do
        Append(desc," x ");
        if Intersection(descs[i],"x:.*wr") <> ""
        then descs[i] := Concatenation("(",descs[i],")"); fi;
        Append(desc,descs[i]);
      od;
      if short then RemoveCharacters(desc," "); fi;
      return desc;
    fi;
    if IsTame(G) then
      if IsFinite(G) then
        return StructureDescription(Image(IsomorphismPermGroup(G)));
      else
        if Length(gens) = 1 then return "Z"; fi;
        rank := RankOfKernelOfActionOnRespectedPartition(G);
        if   IsClassWiseOrderPreserving(G)
        then H := ActionOnRespectedPartition(G);
        else H := Action(G,RefinedRespectedPartitions(G)[2]); fi;
        top := StructureDescription(H);
        if short then bottom := Concatenation("Z^",String(rank)); else
          bottom := "Z";
          for i in [2..rank] do bottom := Concatenation(bottom," x Z"); od;
        fi;
        if not IsTrivial(H) then
          if   Intersection(top,"x:.") <> ""
          then top := Concatenation("(",top,")"); fi;
          if   Position(bottom,'x') <> fail
          then bottom := Concatenation("(",bottom,")"); fi;
          if short then
            desc := Concatenation(bottom,".",top);
          else
            desc := Concatenation(bottom," . ",top);
          fi;
        else desc := bottom; fi;
        return desc;
      fi;
    else
      if Length(gens) = 1 then return "Z"; fi;
      if   HasRankOfFreeGroup(G)
      then return Concatenation("F",String(RankOfFreeGroup(G))); fi;
      bound  := Lcm(List(gens,Modulus)); # some `reasonable' value
      domain := Intersection(Support(G),[-bound..bound]);
      domain := Concatenation(ShortOrbits(G,domain,100));
      if not IsEmpty(domain) then
        induced := Action(G,domain);
        top     := StructureDescription(induced);
        if   Intersection(top,"x:.") <> ""
        then top := Concatenation("(",top,")"); fi;
        desc := Concatenation("<unknown> . ",top);
      else
        if IsClassWiseOrderPreserving(G)
          and not ForAll(gens,gen->Determinant(gen)=0)
        then desc := "<unknown> . Z";
        elif not ForAll(gens,gen->Sign(gen)=1)
        then desc := "<unknown> . 2";
        else desc := "<unknown>"; fi;
      fi;
      if short then RemoveCharacters(desc," "); fi;
      return desc;
    fi;
  end );

#############################################################################
##
#M  StructureDescription( <G> ). . . . . . . . . . . . . . .  for rcwa groups
##
InstallMethod( StructureDescription,
               "for rcwa groups (RCWA)", true, [ IsRcwaGroup ], 0,

  function ( G )
    if   IsNaturalRCWA_Z(G) or IsNaturalRCWA_Z_pi(G) or IsNaturalRCWA_GFqx(G)
    then return Name(G); fi;
    if   IsFinite(G)
    then return StructureDescription(Image(IsomorphismPermGroup(G))); fi;
    return "<unknown>";
  end );

#############################################################################
##
#S  Loading data libraries. /////////////////////////////////////////////////
##
#############################################################################

#############################################################################
##
#F  LoadRCWAExamples( ) . . . . . . . . . . . . . . .  load examples database
##
InstallGlobalFunction( LoadRCWAExamples,

  function ( )
    if IsBoundGlobal("RCWAExamples") then
      Info(InfoRCWA,1,"The examples were already loaded.");
    fi;
    AssignGlobalNC( "RCWAExamples",
                    ReadAsFunction(
                      Concatenation(PackageInfo("rcwa")[1].InstallationPath,
                                    "/examples/examples.g"))() );
    return "RCWAExamples";
  end );

#############################################################################
##
#F  LoadDatabaseOfProductsOf2ClassTranspositions( )
##
InstallGlobalFunction( LoadDatabaseOfProductsOf2ClassTranspositions,
            
  function ( )
    if IsBoundGlobal("CTProducts") then
      Info(InfoRCWA,1,"The requested database was already loaded.");
    fi;
    AssignGlobalNC( "CTProducts",
                    ReadAsFunction(
                      Concatenation(PackageInfo("rcwa")[1].InstallationPath,
                                    "/data/ctproducts/ctprodclass.g"))() );
    return "CTProducts";
  end );

#############################################################################
##
#F  LoadDatabaseOfNonbalancedProductsOfClassTranspositions( )
##
InstallGlobalFunction(
  LoadDatabaseOfNonbalancedProductsOfClassTranspositions,

  function ( )
    if IsBoundGlobal("CTProductsNB") then
      Info(InfoRCWA,1,"The requested database was already loaded.");
    fi;
    AssignGlobalNC( "CTProductsNB",
                    ReadAsFunction(
                      Concatenation(PackageInfo("rcwa")[1].InstallationPath,
                                    "/data/ctproducts/ctprods.g"))() );
    return "CTProductsNB";
  end );

#############################################################################
##
#F  LoadDatabaseOfGroupsGeneratedBy3ClassTranspositions( max_m )
#F  LoadDatabaseOfGroupsGeneratedBy3ClassTranspositions( )
##
InstallGlobalFunction( LoadDatabaseOfGroupsGeneratedBy3ClassTranspositions,

  function ( arg )

    local  data, mods, partlengths, sizes, sizesset, sizespos, i, j, k;

    if not arg in [[],[6],[9]] then
      Error("argument must be either 6 or 9, or left away");
    fi;

    if arg = [] or arg = [6] then
      if IsBoundGlobal("3CTsGroups6") then
        Info(InfoRCWA,1,"The requested database was already loaded.");
      fi;
      AssignGlobalNC( "3CTsGroups6",
                      ReadAsFunction(
                      Concatenation(PackageInfo("rcwa")[1].InstallationPath,
                                    "/data/3ctsgroups6/database.g"))() );
      return "3CTsGroups6";
    elif arg = [9] then
      if IsBoundGlobal("3CTsGroups9") then
        Info(InfoRCWA,1,"The requested database was already loaded.");
      fi;
      data := ReadAsFunction(
                Concatenation(PackageInfo("rcwa")[1].InstallationPath,
                             "/data/3ctsgroups9/database.g"))();
      mods := data.mods;
      partlengths := data.partlengths;
      sizesset := data.sizesset;
      sizespos := data.sizespos;
      sizes := sizespos; # avoid copying(?)
      for i in [3..264] do
        for j in [2..i-1] do
          for k in [1..j-1] do
            if   not IsBound(mods[i][j][k])
            then mods[i][j][k] := 0; fi;
            if   not IsBound(partlengths[i][j][k])
            then partlengths[i][j][k] := 0; fi;
            if   not IsBound(sizespos[i][j][k])
            then sizespos[i][j][k] := 1; fi;
            sizes[i][j][k] := sizesset[sizespos[i][j][k]];
          od;
        od;
      od;
      AssignGlobalNC( "3CTsGroups9",
                      rec( cts         := data.cts,
                           mods        := mods,
                           partlengths := partlengths,
                           sizes       := sizes,
                           All3CTs9Indices := data.All3CTs9Indices,
                           All3CTs9Groups  := data.All3CTs9Groups ) );
      return "3CTsGroups9";
    else return fail; fi;
  end );

#############################################################################
##
#F  LoadDatabaseOfGroupsGeneratedBy4ClassTranspositions( )
##
InstallGlobalFunction( LoadDatabaseOfGroupsGeneratedBy4ClassTranspositions,

  function ( )
    if IsBoundGlobal("4CTsGroups6") then
      Info(InfoRCWA,1,"The requested database was already loaded.");
    fi;
    AssignGlobalNC( "4CTsGroups6",
                    ReadAsFunction(
                      Concatenation(PackageInfo("rcwa")[1].InstallationPath,
                                    "/data/4ctsgroups6/database.g"))() );
    return "4CTsGroups6";
  end );

#############################################################################
##
#F  LoadDatabaseOfCTGraphs( )
##
InstallGlobalFunction( LoadDatabaseOfCTGraphs,

  function ( )
    if IsBoundGlobal("CTGraphs") then
      Info(InfoRCWA,1,"The requested database was already loaded.");
    fi;
    AssignGlobalNC( "CTGraphs",
                    ReadAsFunction(
                      Concatenation(PackageInfo("rcwa")[1].InstallationPath,
                                    "/data/ctproducts/ct-graphs.g"))() );
    return "CTGraphs";
  end );

#############################################################################
##
#E  rcwagrp.gi . . . . . . . . . . . . . . . . . . . . . . . . . .  ends here

[Seitenstruktur0.197Druckenetwas mehr zur Ethik2026-04-27]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge