Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/fr/gap/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 11.0.2024 mit Größe 112 kB image not shown  

Quelle  group.gi   Sprache: unbekannt

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

#############################################################################
##
#W group.gi                                                 Laurent Bartholdi
##
##
#Y Copyright (C) 2006, Laurent Bartholdi
##
#############################################################################
##
##  This file implements the category of functionally recursive groups.
##
#############################################################################

#############################################################################
##
#O SEARCH@
##
SEARCH@.DEPTH := function()
    local v;
    v := ValueOption("FRdepth");
    if v=fail then return SEARCH@.depth; else return v; fi;
end;

SEARCH@.VOLUME := function()
    local v;
    v := ValueOption("FRvolume");
    if v=fail then return SEARCH@.volume; else return v; fi;
end;

SEARCH@.BALL := 1;
SEARCH@.QUOTIENT := 2;

SEARCH@.INIT := function(G)
    # initializes search structure, stored in semigroup G
    if IsBound(G!.FRData) then return; fi;
    if IsFRGroup(G) then
        G!.FRData := rec(pifunc := EpimorphismPermGroup,
                         sphere := [[One(G)],Difference(Set(Union(GeneratorsOfGroup(G),List(GeneratorsOfGroup(G),Inverse))),[One(G)])]);
    elif IsFRMonoid(G) then
        G!.FRData := rec(pifunc := EpimorphismTransformationMonoid,
                         sphere := [[One(G)],Difference(Set(GeneratorsOfSemigroup(G)),[One(G)])]);
    elif IsFRSemigroup(G) then
        G!.FRData := rec(pifunc := EpimorphismTransformationSemigroup,
                         sphere := [[],Set(GeneratorsOfSemigroup(G))]);
    fi;
    G!.FRData.radius := 1;
    G!.FRData.runtimes := [0,0];
    G!.FRData.level := 0;
    G!.FRData.pi := G!.FRData.pifunc(G,0);
    G!.FRData.volume := Sum(List(G!.FRData.sphere,Length));
    G!.FRData.index := Size(Image(G!.FRData.pi));
    if IsFRGroup(G) and (HasIsBoundedFRSemigroup(G) or ForAll(GeneratorsOfGroup(G),IsMealyElement)) and IsBoundedFRSemigroup(G) and not IsFinitaryFRSemigroup(G) then
        G!.FRData.pifunc := EpimorphismGermGroup;
    fi;
end;

SEARCH@.RESET := function(G)
    Unbind(G!.FRData);
end;

SEARCH@.ERROR := function(G,str)
    # allow user to increase the search limits
    local obm, volume, depth;
    obm := OnBreakMessage;
    volume := fail; depth := fail;
    OnBreakMessage := function()
        Print("current limits are (volume = ",SEARCH@.VOLUME(),
              ", depth = ",SEARCH@.DEPTH(),")\n",
              "to increase search volume, type 'volume := <value>; return;'\n",
              "to increase search depth, type 'depth := <value>; return;'\n",
              "type 'quit;' if you want to abort the computation.\n");
        OnBreakMessage := obm;
    end;
    Error("Search for ",G," reached its limits in function ",str,"\n");
    if volume <> fail then PushOptions(rec(FRvolume := volume)); fi;
    if depth <> fail then PushOptions(rec(FRdepth := depth)); fi;
end;

SEARCH@.EXTEND := function(arg)
    # extend the search structure. argument1 is a group; argument2 is optional,
    # and is SEARCH@.BALL to extend search ball radius,
    # and is SEARCH@.QUOTIENT to extend search depth.
    # returns fail if the search limits do not allow extension.
    local i, j, k, l, d, r, strategy;
    d := arg[1]!.FRData;
    if Length(arg)=2 then
        strategy := [arg[2]];
    else
        strategy := [SEARCH@.BALL,SEARCH@.QUOTIENT]; fi;
    if d.volume>=SEARCH@.VOLUME() then
        strategy := Difference(strategy,[SEARCH@.BALL]);
    fi;
    if d.level>=SEARCH@.DEPTH() then
        strategy := Difference(strategy,[SEARCH@.QUOTIENT]);
    fi;
    if Length(strategy)=0 then
        return fail;
    elif Length(strategy)=1 then
        strategy := strategy[1];
    else
        if Maximum(d.runtimes)>5*Minimum(d.runtimes) then
            # at least 20% on each strategy
            strategy := Position(d.runtimes,Minimum(d.runtimes));
        elif d.index > d.volume^2 then
            strategy := SEARCH@.BALL;
        else
            strategy := SEARCH@.QUOTIENT;
        fi;
    fi;
    r := Runtime();
    if strategy=SEARCH@.BALL then
        d.radius := d.radius+1;
        d.sphere[d.radius+1] := [];
        if IsFRGroup(arg[1]) then
            for i in d.sphere[2] do
                for j in d.sphere[d.radius] do
                    k := i*j;
                    if not (k in d.sphere[d.radius-1] or k in d.sphere[d.radius]) then
                        AddSet(d.sphere[d.radius+1],k);
                    fi;
                od;
            od;
        else
            for i in d.sphere[2] do
                for j in d.sphere[d.radius] do
                    k := i*j;
                    for l in [1..d.radius] do
                        if k in d.sphere[l] then k := fail; break; fi;
                    od;
                    if k<>fail then
                        AddSet(d.sphere[d.radius+1],k);
                    fi;
                od;
            od;
        fi;
        MakeImmutable(d.sphere[d.radius+1]);
        d.volume := d.volume + Length(d.sphere[d.radius+1]);
        if d.sphere[d.radius+1]=[] then
            d.volume := d.volume+10^9; # force quotient searches
#            d.runtimes[strategy] := d.runtimes[strategy]+10^9; # infinity messes up arithmetic later
        fi;
    elif strategy=SEARCH@.QUOTIENT then
        d.level := d.level+1;
        d.pi := d.pifunc(arg[1],d.level);
        if IsPcpGroup(Range(d.pi)) then
            d.index := 2^Length(Pcp(Range(d.pi))); # exponential complexity
        elif IsPcGroup(Range(d.pi)) then           # in length of pcgs
            d.index := 2^Length(Pcgs(Range(d.pi)));
        else
            d.index := Size(Image(d.pi));
        fi;
    fi;
    d.runtimes[strategy] := d.runtimes[strategy]+Runtime()-r;
    return true;
end;

SEARCH@.IN := function(x,G)
    # check in x is in G. can return true, false or fail
    if not x^G!.FRData.pi in Image(G!.FRData.pi) then
        return false;
    elif ForAny(G!.FRData.sphere,s->x in s) then
        return true;
    fi;
    return fail;
end;

SEARCH@.CONJUGATE := function(G,x,y)
    # check if x,y is conjugate in G. can return true, false or fail
    if not IsConjugate(Range(G!.FRData.pi),x^G!.FRData.pi,y^G!.FRData.pi) then
        return false;
    elif ForAny(G!.FRData.sphere,s->ForAny(s,t->x^t=y)) then
        return true;
    fi;
    return fail;
end;

SEARCH@.CONJUGATE_WITNESS := function(G,x,y)
    # check if x,y is conjugate in G. can return a conjugator, false or fail
    local s,t;
    if not IsConjugate(Range(G!.FRData.pi),x^G!.FRData.pi,y^G!.FRData.pi) then
        return false;
    else
        for s in G!.FRData.sphere do
            for t in s do
                if x^t=y then
                    return t;
                fi;
            od;
        od;
    fi;
    return fail;
end;

SEARCH@.CONJUGATE_COSET := function(G,c,x,y)
    # check if x,y is conjugate in c can return a conjugator, false or fail
    local s,t,r,B,K,K_pi;
    B := BranchStructure(G);
    K := BranchingSubgroup(G);
    K_pi := Image(G!.FRData.pi,K);
    if IsOne(x) and IsOne(y) then
     return PreImagesRepresentativeNC(B.quo,c);
    fi;
    r := RepresentativeAction(Range(G!.FRData.pi),x^G!.FRData.pi,y^G!.FRData.pi);
    if r = fail then
     return false;
    fi;
    if not PreImagesRepresentativeNC(B.quo,c)^G!.FRData.pi in Union(List(K_pi,z->RightCoset(Centralizer(Range(G!.FRData.pi),x^G!.FRData.pi),r*z))) then
        return false;
    else
        for s in G!.FRData.sphere do
            for t in s do
                if x^t=y and t^B.quo = c then
                    return t;
                fi;
            od;
        od;
    fi;
    return fail;
end;

SEARCH@.EXTENDTRANSVERSAL := function(G,H,trans)
    # completes the tranversal trans of H^pi in G^pi, and returns it,
    # or "fail" if the search volume limit of G is too small.
    # trans is a partial transversal.
    local pitrans, got, todo, i, j, k;
    pitrans := RightTransversal(Image(G!.FRData.pi),Image(G!.FRData.pi,H));
    got := []; todo := Length(pitrans);
    for i in trans do
        got[PositionCanonical(pitrans,i^G!.FRData.pi)] := i;
        todo := todo-1;
    od;
    if todo=0 then return got; fi;
    i := 1;
    while true do
        if not IsBound(G!.FRData.sphere[i]) and SEARCH@.EXTEND(G,SEARCH@.BALL)=fail then
            return fail;
        fi;
        for j in G!.FRData.sphere[i] do
            k := PositionCanonical(pitrans,j^G!.FRData.pi);
            if not IsBound(got[k]) then
                got[k] := j;
                Add(trans,j);
                todo := todo-1;
                if todo=0 then return got; fi;
            fi;
        od;
        i := i+1;
    od;
    return fail;
end;

SEARCH@.CHECKTRANSVERSAL := function(G,H,trans)
    # check that trans is a transversal of H in G.
    # returns true on success, false on failure, and fail if the search
    # volume of H is too limited.
    local g, t, u, b, transinv, found;
    transinv := List(trans,Inverse);
    for g in G!.FRData.sphere[2] do for t in trans do
        repeat
            found := false;
            for u in transinv do
                b := SEARCH@.IN(t*g*u,H);
                if b=true then
                    found := true;
                    break;
                elif b=fail then
                    found := fail;
                fi;
            od;
            if found=false then
                return false;
            elif found=fail and SEARCH@.EXTEND(H)=fail then
                return fail;
            fi;
        until found=true;
    od; od;
    return true;
end;
#############################################################################

#############################################################################
##
#O SCGroup( <M> )
#O SCSemigroup( <M> )
#O SCMonoid( <M> )
##

# we'd prefer the method that uses an finitely L-presented group isomorphic to G;
# but maybe NQL is not loaded. If so, we'll content ourselves with a
# finitely presented group that maps onto G. In all cases, we expect the
# PreImageData structure to return such a group with the correct abelianization.

# the next method is also there to cache an attribute giving the data required
# to express group elements as words.
if @.nql then
    InstallMethod(FRGroupImageData, "(FR) for a FR group with preimage data",
            [IsFRGroup and HasFRGroupPreImageData],
            G->FRGroupPreImageData(G)(-1));
else
    InstallMethod(FRGroupImageData, "(FR) for a FR group with preimage data",
            [IsFRGroup and HasFRGroupPreImageData],
            G->FRGroupPreImageData(G)(0));
fi;

InstallAccessToGenerators(IsFRGroup,
        "(FR) for a FR group",GeneratorsOfGroup);

InstallAccessToGenerators(IsFRMonoid,
        "(FR) for a FR monoid",GeneratorsOfMonoid);

InstallAccessToGenerators(IsFRSemigroup,
        "(FR) for a FR semigroup",GeneratorsOfSemigroup);

InstallMethod(SCGroupNC, "(FR) for a Mealy machine",
        [IsMealyMachine],
        function(M)
    local G;
    G := Group(List(GeneratorsOfFRMachine(M),s->FRElement(M,s)));
    SetAlphabetOfFRSemigroup(G,AlphabetOfFRObject(M));
    SetIsStateClosed(G,true);
    SetCorrespondence(G,StateSet(M));
    SetUnderlyingFRMachine(G,M);
    return G;
end);

InstallMethod(SCGroupNC, "(FR) for a FR machine",
        [IsFRMachine and IsFRMachineStdRep],
        function(M)
    local G;
    G := Group(List(GeneratorsOfFRMachine(M),s->FRElement(M,s)));
    SetAlphabetOfFRSemigroup(G,AlphabetOfFRObject(M));
    SetIsStateClosed(G,true);
    SetCorrespondence(G,GroupHomomorphismByFunction(M!.free,G,w->FRElement(M,w)));
    SetUnderlyingFRMachine(G,M);
    return G;
end);

InstallMethod(SCMonoidNC, "(FR) for a Mealy machine",
        [IsMealyMachine],
        function(M)
    local G;
    G := Monoid(List(GeneratorsOfFRMachine(M),s->FRElement(M,s)));
    SetAlphabetOfFRSemigroup(G,AlphabetOfFRObject(M));
    SetIsStateClosed(G,true);
    SetCorrespondence(G,StateSet(M));
    SetUnderlyingFRMachine(G,M);
    return G;
end);

InstallMethod(SCMonoidNC, "(FR) for a FR machine",
        [IsFRMachine and IsFRMachineStdRep],
        function(M)
    local G;
    G := Monoid(List(GeneratorsOfFRMachine(M),s->FRElement(M,s)));
    SetAlphabetOfFRSemigroup(G,AlphabetOfFRObject(M));
    SetIsStateClosed(G,true);
    SetCorrespondence(G,MagmaHomomorphismByFunctionNC(M!.free,G,w->FRElement(M,w)));
    SetUnderlyingFRMachine(G,M);
    return G;
end);

InstallMethod(SCSemigroupNC, "(FR) for a Mealy machine",
        [IsMealyMachine],
        function(M)
    local G;
    G := Semigroup(List(GeneratorsOfFRMachine(M),s->FRElement(M,s)));
    SetAlphabetOfFRSemigroup(G,AlphabetOfFRObject(M));
    SetIsStateClosed(G,true);
    SetCorrespondence(G,StateSet(M));
    SetUnderlyingFRMachine(G,M);
    return G;
end);

InstallMethod(SCSemigroupNC, "(FR) for a FR machine",
        [IsFRMachine],
        function(M)
    local G;
    G := Semigroup(List(GeneratorsOfFRMachine(M),s->FRElement(M,s)));
    SetAlphabetOfFRSemigroup(G,AlphabetOfFRObject(M));
    SetIsStateClosed(G,true);
    SetCorrespondence(G,MagmaHomomorphismByFunctionNC(M!.free,G,w->FRElement(M,w)));
    SetUnderlyingFRMachine(G,M);
    return G;
end);

InstallMethod(SCGroup, "(FR) for a FR machine",
        [IsFRMachine],
        function(M)
    local gens, corr, i, x, G;
    gens := []; corr := [];
    for i in GeneratorsOfFRMachine(M) do
        x := FRElement(M,i);
        if IsOne(x) then
            Add(corr,0);
        elif x in gens then
            Add(corr,Position(gens,x));
        elif x^-1 in gens then
            Add(corr,-Position(gens,x^-1));
        else
            Add(gens,x);
            Add(corr,Size(gens));
        fi;
    od;
    if gens=[] then
        G := TrivialSubgroup(Group(FRElement(M,GeneratorsOfFRMachine(M)[1])));
    else
        G := Group(gens);
    fi;
    SetAlphabetOfFRSemigroup(G,AlphabetOfFRObject(M));
    SetIsStateClosed(G,true);
    if IsMealyMachine(M) then
        SetCorrespondence(G,corr);
    elif IsFRMachineStdRep(M) then
        SetCorrespondence(G,GroupHomomorphismByFunction(M!.free,G,w->FRElement(M,w)));
    fi;
    SetUnderlyingFRMachine(G,M);
    return G;
end);

InstallMethod(SCMonoid, "(FR) for a FR machine",
        [IsFRMachine],
        function(M)
    local gens, corr, i, x, G;
    gens := []; corr := [];
    for i in GeneratorsOfFRMachine(M) do
        x := FRElement(M,i);
        if IsOne(x) then
            Add(corr,0);
        elif x in gens then
            Add(corr,Position(gens,x));
        else
            Add(gens,x);
            Add(corr,Size(gens));
        fi;
    od;
    if gens=[] then
        G := TrivialSubmonoid(Monoid(FRElement(M,GeneratorsOfFRMachine(M)[1])));
    else
        G := Monoid(gens);
    fi;
    SetAlphabetOfFRSemigroup(G,AlphabetOfFRObject(M));
    SetIsStateClosed(G,true);
    if IsMealyMachine(M) then
        SetCorrespondence(G,corr);
    elif IsFRMachineStdRep(M) then
        SetCorrespondence(G,MagmaHomomorphismByFunctionNC(M!.free,G,w->FRElement(M,w)));
    fi;
    SetUnderlyingFRMachine(G,M);
    return G;
end);

InstallMethod(SCSemigroup, "(FR) for a FR machine",
        [IsFRMachine],
        function(M)
    local gens, corr, i, x, G;
    gens := []; corr := [];
    for i in GeneratorsOfFRMachine(M) do
        x := FRElement(M,i);
        if x in gens then
            Add(corr,Position(gens,x));
        else
            Add(gens,x);
            Add(corr,Size(gens));
        fi;
    od;
    G := Semigroup(gens);
    SetAlphabetOfFRSemigroup(G,AlphabetOfFRObject(M));
    SetIsStateClosed(G,true);
    if IsMealyMachine(M) then
        SetCorrespondence(G,corr);
    elif IsFRMachineStdRep(M) then
        SetCorrespondence(G,MagmaHomomorphismByFunctionNC(M!.free,G,w->FRElement(M,w)));
    fi;
    SetUnderlyingFRMachine(G,M);
    return G;
end);
#############################################################################

#############################################################################
##
#O FullSCGroup
#O FullSCSemigroup
#O FullSCMonoid
##
FILTERORDER@ := [IsFRObject, IsFinitaryFRElement, IsBoundedFRElement, IsPolynomialGrowthFRElement, IsFiniteStateFRElement, IsFRElement];
# value IsFRObject means a group for which the exact category of elements is
# not known; it really stand for "unspecified subgroup of FullSCGroup"

BindGlobal("FULLGETDATA@", function(arglist,
        cat, Iscat, IsFRcat, GeneratorsOfcat, AscatFRElement,
        makevertex, stype)
    local a, G, rep, alphabet, i, x, name, filter, depth, vertex, o, onerep;
    filter := IsFRElement;
    depth := infinity;
    for a in arglist do
        if Iscat(a) then
            vertex := a;
        elif IsList(a) or IsDomain(a) then
            alphabet := a;
        elif IsFilter(a) then
            if Position(FILTERORDER@,a)<Position(FILTERORDER@,filter) then filter := a; fi;
        elif IsInt(a) then
            depth := a;
        else
            Error("Unknown argument ",a,"\n");
        fi;
    od;
    if not IsBound(alphabet) then
        if IsBound(vertex) and IsSemigroup(vertex) then
            alphabet := [1..LargestMovedPoint(vertex)];
        else
            Error("Please specify at least a vertex group or an alphabet\n");
        fi;
    elif not IsBound(vertex) then
        vertex := makevertex(alphabet);
    fi;
    rep := First(GeneratorsOfSemigroup(vertex),x->not IsOne(x));
    if rep=fail then rep := Representative(vertex); fi;
    onerep := One(vertex);
    if onerep=fail then
        onerep := rep;
    fi; # maybe this does not define an element of the semigroup :(
    if IsList(alphabet) then
        rep := MealyElement([List(alphabet,a->2),List(alphabet,a->2)],[rep,onerep],1);
    else
        rep := MealyElement(Domain([1,2]),alphabet,function(s,a) return 1; end, function(s,a) if s=1 then return a; else return a^Representative(vertex); fi; end);
    fi;
    if depth < infinity then filter := IsFinitaryFRElement; fi;
    if filter=IsFRElement then rep := AscatFRElement(rep); fi;
    G := Objectify(NewType(FamilyObj(cat(rep)),
                 IsFRcat and IsAttributeStoringRep),
                 rec());
    SetAlphabetOfFRSemigroup(G,alphabet);
    SetDepthOfFRSemigroup(G,depth);
    if IsOne(onerep) then
        SetOne(G,One(rep));
    else
        SetOne(G,fail);
    fi;
    if ((IsList(alphabet) or HasSize(alphabet)) and Size(alphabet)=1) or not IsBound(Enumerator(vertex)[2]) or depth=0 then
        SetIsTrivial(G, true);
        SetIsFinite(G, true);
        Setter(GeneratorsOfcat)(G,[]);
        if Size(vertex)=0 or (One(G)=fail and depth<>infinity) then
            SetSize(G,0);
        else
            SetSize(G,1);
            SetRepresentative(G,rep);
        fi;
    else
        if filter<>IsFRObject and (onerep<>rep or depth=infinity) then
            SetRepresentative(G,rep); # if finite depth and semigroup,
            # maybe there are no representative
        fi;
        SetIsTrivial(G, false);
        if depth=infinity then
            SetIsFinite(G, false);
            SetSize(G, infinity);
        elif IsList(alphabet) or HasSize(alphabet) then
            SetIsFinite(G, true);
            SetSize(G, Size(vertex)^((Size(alphabet)^depth-1)/(Size(alphabet)-1)));
            x := GeneratorsOfcat(vertex);
            x := List(x,g->MealyElement([List(alphabet,a->2),List(alphabet,a->2)],[g,One(vertex)],1));
            if cat=Group then
                o := List(Orbits(vertex,alphabet),Representative);
            else
                o := alphabet;
            fi;
            a := x;
            for i in [1..depth-1] do
                x := Concatenation(List(x,g->List(o,a->VertexElement(a,g))));
                Append(a,x);
            od;
            Setter(GeneratorsOfcat)(G,a);
            if cat=Group then
                Append(a,List(a,Inverse));
                Setter(GeneratorsOfSemigroup)(G,a);
            fi;
        fi;
    fi;
    if filter=IsFRObject then
        name := "<recursive ";
        Append(name,LowercaseString(stype));
        Append(name," over ");
        Append(name,String(alphabet));
        Append(name,">");
    else
        name := "FullSC";
        Append(name,stype);
        Append(name,"(");
        Append(name,String(alphabet));
        if vertex<>makevertex(alphabet) then
            Append(name,", "); PrintTo(OutputTextString(name,true),vertex);
        fi;
        if depth < infinity then
            Append(name,", "); Append(name,String(depth));
        elif filter <> IsFRElement then
            Setter(filter)(G,true);
            x := ""; PrintTo(OutputTextString(x,false),filter);
            if x{[1..10]}="<Operation" then x := x{[13..Length(x)-2]}; fi;
            Append(name,", "); Append(name,x);
        fi;
        Append(name,")");
        for x in [2..Position(FILTERORDER@,filter)-1] do
            Setter(FILTERORDER@[x])(G,false);
        od;
        SetIsStateClosed(G, true);
        SetIsRecurrentFRSemigroup(G, depth=infinity);
        SetIsBranched(G, depth=infinity);
        if depth<infinity and cat=Group then
            SetBranchingSubgroup(G,TrivialSubgroup(G));
            x := EpimorphismPermGroup(G,depth);
            SetIsInjective( x, true );
            SetIsHandledByNiceMonomorphism(G,true);
            SetNiceMonomorphism(G,x);
        else
            SetBranchingSubgroup(G,G);
        fi;
        if filter=IsFinitaryFRElement then
            if One(G)=fail then
                SetNucleusOfFRSemigroup(G,[]);
            else
                SetNucleusOfFRSemigroup(G,[One(G)]);
            fi;
        else
            SetNucleusOfFRSemigroup(G,G);
        fi;
        SetIsContracting(G,filter=IsFinitaryFRElement or filter=IsBoundedFRElement);
        SetHasOpenSetConditionFRSemigroup(G,filter=IsFinitaryFRElement);
    fi;
    SetFullSCVertex(G,vertex);
    SetFullSCFilter(G,filter);
    SetName(G,name);
    return G;
end);

InstallGlobalFunction(FullSCGroup, # "(FR) full tree automorphism group",
        function(arg)
    local  G;
    G := FULLGETDATA@(arg,Group,IsGroup,IsFRGroup,GeneratorsOfGroup,AsGroupFRElement,
                 SymmetricGroup,"Group");
    if IsTrivial(G) or FullSCFilter(G)=IsFRObject then
        return G;
    elif DepthOfFRSemigroup(G)=infinity then
        SetIsLevelTransitiveFRGroup(G,IsTransitive(FullSCVertex(G),AlphabetOfFRSemigroup(G)));
        SetIsFinitelyGeneratedGroup(G, false);
        SetCentre(G, TrivialSubgroup(G));
        SetIsSolvableGroup(G, false);
        SetIsAbelian(G, false);
    fi;
    SetIsPerfectGroup(G, IsPerfectGroup(FullSCVertex(G)));
    return G;
end);

InstallGlobalFunction(FullSCMonoid,
        function(arg)
    local M;
    M := FULLGETDATA@(arg,Monoid,IsMonoid,IsFRMonoid,GeneratorsOfMonoid,AsMonoidFRElement,
                 FullTransformationMonoid,
                 "Monoid");
    return M;
end);

InstallGlobalFunction(FullSCSemigroup,
        function(arg)
    local S;
    S := FULLGETDATA@(arg,Semigroup,IsSemigroup,IsFRSemigroup,GeneratorsOfSemigroup,AsSemigroupFRElement,
                 FullTransformationSemigroup,"Semigroup");
    return S;
end);

InstallTrueMethod(HasFullSCData, HasFullSCVertex and HasFullSCFilter);
#############################################################################

#############################################################################
##
#M AlphabetOfFRSemigroup
##
InstallMethod(AlphabetOfFRSemigroup, "(FR) for an FR semigroup",
        [IsFRSemigroup],
        function(G)
    local p;
    while not HasRepresentative(G) and HasParent(G) and not IsIdenticalObj(Parent(G),G) do
        G := Parent(G);
    od;
    return AlphabetOfFRObject(Representative(G));
end);
#############################################################################

#############################################################################
##
#M IsGeneratorsOfMagmaWithInverses
##
InstallMethod(IsGeneratorsOfMagmaWithInverses, "(FR) for a list of FR elements",
        [IsListOrCollection],
        function(L)
    if ForAll(L,IsFRElement) and ForAll(L,IsInvertible) then return true;
    else TryNextMethod(); fi;
end);
#############################################################################

#############################################################################
##
#M  Random
#M  Pseudorandom
##
BindGlobal("RANDOMFINITARY@", function(G,d)
    local i, a, t, transitions, output;
    if d=0 then return One(G); fi;
    transitions := [];
    output := [Random(FullSCVertex(G))];
    for i in [0..d-2] do for i in [1..Size(AlphabetOfFRSemigroup(G))^i] do
        t := [];
        for a in AlphabetOfFRSemigroup(G) do
            Add(output,Random(FullSCVertex(G)));
            Add(t,Length(output));
        od;
        Add(transitions,t);
    od; od;
    Add(output,One(FullSCVertex(G)));
    for i in [0..Size(AlphabetOfFRSemigroup(G))^(d-1)] do
        Add(transitions,List(AlphabetOfFRSemigroup(G),a->Length(output)));
    od;
    return MealyElement(transitions,output,1);
end);

BindGlobal("RANDOMBOUNDED@", function(G)
    local E, F, M, s, n, i, j;
    if IsTrivial(G) then
        return One(G);
    fi;
    s := Size(AlphabetOfFRSemigroup(G));
    F := RANDOMFINITARY@(G,Random([2..4]));
    M := UnderlyingFRMachine(F);
    for i in [0..5] do
        n := Random([1..4]);
        E := MealyMachineNC(FRMFamily(AlphabetOfFRSemigroup(G)),List([1..n],i->List([1..s],i->Random(n+[1..M!.nrstates]))),List([1..n],i->ListTransformation(Random(FullSCVertex(G)),s)))+M;
        for j in [1..n] do
            E!.transitions[j][Random([1..s])] := 1+RemInt(j,n);
        od;
        F := F*FRElement(E,1);
    od;
    return F;
end);

BindGlobal("RANDOMPOLYNOMIALGROWTH@", function(G)
    local E, F, i, j, one, n, p;
#    F := RANDOMBOUNDED@(G);
    for i in [0..5] do
        E := UnderlyingFRMachine(RANDOMBOUNDED@(G));
        for j in [1..Random([0..4])] do
            one := First([1..E!.nrstates],i->IsOne(FRElement(E,i)));
            repeat
                n := Random([1..E!.nrstates]);
                p := Random([1..Size(AlphabetOfFRSemigroup(G))]);
            until E!.transitions[n][p]=one;
            E := E+UnderlyingFRMachine(RANDOMBOUNDED@(G));
            E!.transitions[n^Correspondence(E)[1]][p] := 1^Correspondence(E)[2];
        od;
        return FRElement(E,1);
        F := F*E;
    od;
    return F;
end);

InstallMethodWithRandomSource(Random, "for a random source and a full SC Group",
        [IsRandomSource, IsFRSemigroup and HasFullSCData],
        function (rs, G)
    local n, f;
    if DepthOfFRSemigroup(G)<infinity then
        return Minimized(RANDOMFINITARY@(G,DepthOfFRSemigroup(G)));
    elif IsFinitaryFRSemigroup(G) then
        return Minimized(RANDOMFINITARY@(G,Random(rs, 0, 5)));
    elif IsBoundedFRSemigroup(G) then
        return RANDOMBOUNDED@(G);
    elif IsPolynomialGrowthFRSemigroup(G) then
        return RANDOMPOLYNOMIALGROWTH@(G);
    elif IsFiniteStateFRSemigroup(G) then
        n := Random(rs, 1, 20);
        return MealyElement(List([1..n],s->List(AlphabetOfFRSemigroup(G),a->Random(rs, 1, n))),List([1..n],s->Random(rs, FullSCVertex(G))),1);
    else
        n := Random(rs, 1, 5);
        if IsGroup(StateSet(Representative(G))) then
            f := FreeGroup(n);
        else
            f := FreeMonoid(n);
        fi;
        return FRElement(f,List([1..n],s->List(AlphabetOfFRSemigroup(G),a->Random(rs, f))),List([1..n],s->Random(rs, FullSCVertex(G))),Random(rs, f));
    fi;
end);

BindGlobal("INITPSEUDORANDOM@", function(g, len, scramble)
    local gens, seed, i;
    gens := GeneratorsOfSemigroup(g);
    if 0 = Length(gens) then
        SetPseudoRandomSeed(g,[[]]);
        return;
    fi;
    len := Maximum(len,Length(gens),2);
    seed := ShallowCopy(gens);
    for i in [Length(gens)+1..len] do
        seed[i] := Random(gens);
    od;
    SetPseudoRandomSeed(g,[seed]);
    for i in [1..scramble] do
        PseudoRandom(g);
    od;
end);

BindGlobal("PSEUDORANDOM@", function (g)
    local seed, i, j;
    if not HasPseudoRandomSeed(g) then
        i := Length( GeneratorsOfSemigroup(g) );
        INITPSEUDORANDOM@(g, i+10, Maximum(i*10,100));
    fi;
    seed := PseudoRandomSeed(g);
    if 0 = Length(seed[1]) then
        return One(g);
    fi;
    i := Random([1..Length(seed[1])]);
    repeat
        j := Random([1..Length(seed[1])]);
    until i <> j;
    if Random([true, false]) then
        seed[1][j] := seed[1][i] * seed[1][j];
    else
        seed[1][j] := seed[1][j] * seed[1][i];
    fi;
    return seed[1][j];
end);

InstallMethod(PseudoRandom, "(FR) for an FR group",
        [IsFRSemigroup],
        function(g)
    local lim, gens, i, x;
    lim := ValueOption("radius");
    if lim=fail then return PSEUDORANDOM@(g); fi;
    gens := GeneratorsOfSemigroup(g);
    x := Random(gens);
    for i in [1..lim] do x := x*Random(gens); od;
    return x;
end);
#############################################################################

#############################################################################
##
#M  IsSubgroup
#M \in
#M =
#M IsSubset
#M Size
#M IsFinite
#M Iterator
#M Enumerator
##
InstallMethod(IsSubset, "(FR) for two full FR semigroups",
        IsIdenticalObj,
        [IsFRSemigroup and HasFullSCData, IsFRSemigroup and HasFullSCData],
        100, # make sure groups with full SC data come first
        function (G, H)
    if FullSCFilter(G)=IsFRObject then
        return fail;
    elif not IsSubset(FullSCVertex(G), FullSCVertex(H)) then
        return false;
    elif DepthOfFRSemigroup(G)<DepthOfFRSemigroup(H) then
        return false;
    elif DepthOfFRSemigroup(H)<infinity then
        return true;
    elif Position(FILTERORDER@,FullSCFilter(G))<Position(FILTERORDER@,FullSCFilter(H)) then
        return false;
    else
        return true;
    fi;
end);

InstallMethod(IsSubset, "(FR) for an FR semigroup and a full SC semigroup",
        IsIdenticalObj,
        [IsFRSemigroup, IsFRSemigroup and HasFullSCData],
        function (G, H)
    if FullSCFilter(H)=IsFRObject then
        return fail;
    elif DepthOfFRSemigroup(H)<>infinity and
      ForAll(GeneratorsOfSemigroup(H),x->x in G) then
        return true;
    else
        return false;
    fi;
end);

InstallMethod(IsSubset, "(FR) for an FR semigroup and a f.g. FR group",
        IsIdenticalObj,
        [IsFRSemigroup, IsFRGroup and IsFinitelyGeneratedGroup],
        5, # little boost: avoid GAP methods that involve finiteness tests
        function (G, H)
    return IsSubset(G, GeneratorsOfGroup(H));
end);

InstallMethod(IsSubset, "(FR) for an FR semigroup and an FR monoid",
        IsIdenticalObj,
        [IsFRSemigroup, IsFRMonoid and IsFinitelyGeneratedMonoid],
        5, # little boost: avoid GAP methods that involve finiteness tests
        function (G, H)
    return IsSubset(G, GeneratorsOfMonoid(H));
end);

InstallMethod(IsSubset, "(FR) for two FR semigroups",
        IsIdenticalObj,
        [IsFRSemigroup, IsFRSemigroup], # missing IsFinitelyGeneratedSemigroup!
        5, # little boost: avoid GAP methods that involve finiteness tests
        function (G, H)
    return IsSubset(G, GeneratorsOfSemigroup(H));
end);

InstallMethod(\=, "(FR) for two FR semigroups",
        IsIdenticalObj,
        [IsFRSemigroup, IsFRSemigroup],
        5, # little boost: avoid GAP methods that involve finiteness tests
        function (G, H)
    return IsSubset(G, H) and IsSubset(H, G);
end);

# we duplicate the method above for groups, otherwise the generic method
# for groups gets higher priority -- and triggers "IsFinite".
InstallMethod(\=, "(FR) for two FR semigroups",
        IsIdenticalObj,
        [IsFRGroup, IsFRGroup],
        5, # little boost: avoid GAP methods that involve finiteness tests
        function (G, H)
    return IsSubset(G, H) and IsSubset(H, G);
end);

InstallMethod(\in, "(FR) for an FR element and a full SC semigroup",
        IsElmsColls,
        [IsFRElement, IsFRSemigroup and HasFullSCData],
        function ( g, G )
    if FullSCFilter(G)=IsFRObject then
        return fail;
    elif not ForAll(States(g), s->TransformationList(Output(s)) in FullSCVertex(G)) then
        return false;
    elif not FullSCFilter(G)(g) then
        return false;
    elif DepthOfFRSemigroup(G)<>infinity and DepthOfFRElement(g)>DepthOfFRSemigroup(G) then
        return false;
    else
        return true;
    fi;
end);

InstallMethod(\in, "(FR) for an FR element and an FR semigroup",
        IsElmsColls,
        [IsFRElement, IsFRSemigroup],
        function ( g, G )
    local b;
    if HasNucleusOfFRSemigroup(G) then
        if not IsFiniteStateFRElement(g) or not
           IsSubset(NucleusOfFRSemigroup(G),LimitStates(g)) then
            return false;
        fi;
    fi;
    SEARCH@.INIT(G);
    while true do
        b := SEARCH@.IN(g,G);
        if b<>fail then return b; fi;
        while SEARCH@.EXTEND(G)=fail do
            SEARCH@.ERROR(G,"\\in");
        od;
        Info(InfoFR, 3, "\\in: searching at level ",G!.FRData.level," and in sphere of radius ",G!.FRData.radius);
    od;
end);

InstallMethod(IsConjugate, "(FR) for an FR element and an FR group",
        [IsFRGroup,IsFRElement, IsFRElement],
        function ( G, g, h )
    local b;
    SEARCH@.INIT(G);
    while true do
        b := SEARCH@.CONJUGATE(G,g,h);
        if b<>fail then return b; fi;
        while SEARCH@.EXTEND(G)=fail do
            SEARCH@.ERROR(G,"IsConjugate");
        od;
        Info(InfoFR, 3, "IsConjugate: searching at level ",G!.FRData.level," and in sphere of radius ",G!.FRData.radius);
    od;
end);

InstallOtherMethod(RepresentativeActionOp, "(FR) for an FR element and an FR group",
        [IsFRGroup,IsFRElement, IsFRElement, IsFunction],
        function ( G, g, h, f )
    local b;
    if f <> OnPoints then TryNextMethod(); fi;
    SEARCH@.INIT(G);
    while true do
        b := SEARCH@.CONJUGATE_WITNESS(G,g,h);
        if b<>fail then 
         if b=false 
          then return fail; 
         else 
          return b; 
         fi;
        fi;
        while SEARCH@.EXTEND(G)=fail do
            SEARCH@.ERROR(G,"RepresentativeActionOp");
        od;
        Info(InfoFR, 3, "RepresentativeActionOp: searching at level ",G!.FRData.level," and in sphere of radius ",G!.FRData.radius);
    od;
end);

BindGlobal("EDGESTABILIZER@", function(G)
    # returns the stabilizer of an edge in the tree; i.e.
    # computes the action on the second level, and takes the stabilizer of a subtree and at the root.
    local i, a, s;
    a := AlphabetOfFRSemigroup(G);

    s := Stabilizer(PermGroup(G,2),a,OnTuples); # fix [1,1],...,[1,d]
    for i in a do
        s := Stabilizer(s,a+(i-1)*Size(a),OnSets); # preserve {[i,1],...,[i,d]}
    od;
    # the following method is too slow
    #s := Stabilizer(s,List(AlphabetOfFRSemigroup(G),i->(i-1)*Size(AlphabetOfFRSemigroup(G))+AlphabetOfFRSemigroup(G)),OnTuplesSets);
    return s;
end);
    
BindGlobal("ISFINITE_THOMPSONWIELANDT@", function(G)
    # returns 'true' if G is finite, 'false' if not, 'fail' otherwise
    #
    # Thompson-Wielandt's theorem says that G is infinite if the stabilizer of a vertex is primitive
    # and the stabilizer of the star of an edge is not a p-group; see
    # Burger-Mozes, Lattices..., prop 1.3
    local q, s;

    if HasUnderlyingFRMachine(G) and IsBireversible(UnderlyingFRMachine(G))
       and IsPrimitive(VertexTransformations(G),AlphabetOfFRSemigroup(G)) then
        s := EDGESTABILIZER@(G);
        if not IsPGroup(s) then
            return false;
        fi;
    fi;
    return fail;
end);

BindGlobal("ISFINITE_MINIMIZEDUAL@", function(G)
    # keep taking dual and minimizing, ask whether we get the trivial machine
    local gens, m, oldm;
    
    if not HasGeneratorsOfGroup(G) then
        return fail;
    fi;
    
    gens := GeneratorsOfGroup(G);
    if ForAll(gens,HasIsFiniteStateFRElement) and ForAll(gens,IsFiniteStateFRElement) then
        m := Sum(List(gens,UnderlyingFRMachine));
        repeat
            oldm := m;
            m := Minimized(DualMachine(m));
            m := Minimized(DualMachine(m));
        until m=oldm;
        if Size(StateSet(m))=1 then
            return true;
        fi;
    fi;
    return fail;
end);

InstallMethod(IsFinite, "(FR) for an FR group",
        [IsFRGroup],
        function(G)
    local b;

    if IsFinitaryFRSemigroup(G) then
        return true;
    fi;
    
    b := ISFINITE_MINIMIZEDUAL@(G);
    if b<>fail then
        return b;
    fi;

    b := ISFINITE_THOMPSONWIELANDT@(G);
    if b<>fail then
        return b;
    fi;
    
    if IsLevelTransitiveFRGroup(G) then
        return false;
    fi;
    
    TryNextMethod();
end);

BindGlobal("SIZE@", function(G,testorder)
    local n, g, iter;
    iter := Iterator(G);
    n := 0;
    while not IsDoneIterator(iter) do
        g := NextIterator(iter);
        if testorder and Order(g)=infinity then return infinity; fi;
        n := n+1;
        if RemInt(n,100)=0 then
            Info(InfoFR,2,"Size: is at least ",n);
        fi;
    od;
    return n;
end);

InstallMethod(IsTrivial, "(FR) for an FR semigroup",
        [IsFRSemigroup],
        function(G)
    return ForAll(GeneratorsOfSemigroup(G),IsOne);
end);

InstallMethod(IsTrivial, "(FR) for an FR monoid",
        [IsFRMonoid],
        function(G)
    return ForAll(GeneratorsOfMonoid(G),IsOne);
end);

InstallMethod(IsTrivial, "(FR) for an FR group",
        [IsFRGroup],
        function(G)
    return ForAll(GeneratorsOfGroup(G),IsOne);
end);

InstallMethod(Size, "(FR) for an FR semigroup",
        [IsFRSemigroup],
        function(G)
    return SIZE@(G,false);
end);

InstallMethod(Size, "(FR) for an FR group",
        [IsFRGroup],
        function(G)
    local b, gens, rays;
    b := ISFINITE_THOMPSONWIELANDT@(G);
    if b=true then
        return SIZE@(G,false);
    elif b=false then
        return infinity;
    elif IsBoundedFRSemigroup(G) then
        gens := GeneratorsOfGroup(G);
        rays := Union(List(gens,g->List(Germs(g),p->p[1])));
        if ForAll(rays,x->ForAll(gens,s->x^s=x)) then
            return SIZE@(G,false);
        fi;
    fi;
    if IsLevelTransitiveFRGroup(G) then return infinity; fi;
    #!!! try to find a subgroup that acts transitively on a subtree
    return SIZE@(G,true);
end);

SEARCH@.NEXTITERATOR := function(iter)
    if iter!.pos < Length(iter!.G!.FRData.sphere[iter!.radius+1]) then
        iter!.pos := iter!.pos+1;
    else
        iter!.pos := 1;
        while iter!.radius=iter!.G!.FRData.radius and
           SEARCH@.EXTEND(iter!.G,SEARCH@.BALL)=fail do
            SEARCH@.ERROR(iter!.G,"NextIterator");
        od;
        iter!.radius := iter!.radius+1;
        if iter!.G!.FRData.sphere[iter!.radius+1]=[] then return fail; fi;
    fi;
    return iter!.G!.FRData.sphere[iter!.radius+1][iter!.pos];
end;

SEARCH@.ISDONEITERATOR := function(iter)
    if iter!.pos < Length(iter!.G!.FRData.sphere[iter!.radius+1]) then
        return false;
    else
        iter!.pos := 0;
        while iter!.radius=iter!.G!.FRData.radius and
           SEARCH@.EXTEND(iter!.G,SEARCH@.BALL)=fail do
            SEARCH@.ERROR(iter!.G,"IsDoneIterator");
        od;
        iter!.radius := iter!.radius+1;
        return iter!.G!.FRData.sphere[iter!.radius+1]=[];
    fi;
end;

SEARCH@.SHALLOWCOPY := function(iter)
    return rec(
               NextIterator := SEARCH@.NEXTITERATOR,
               IsDoneIterator := SEARCH@.ISDONEITERATOR,
               ShallowCopy := SEARCH@.SHALLOWCOPY,
               G := iter!.G,
               pos := iter!.pos,
               radius := iter!.radius);
end;

SEARCH@.ELEMENTNUMBER := function(iter,n)
    local i;
    i := 1;
    while n > Length(iter!.G!.FRData.sphere[i]) do
        n := n-Length(iter!.G!.FRData.sphere[i]);
        i := i+1;
        while not IsBound(iter!.G!.FRData.sphere[i]) and
           SEARCH@.EXTEND(iter!.G,SEARCH@.BALL)=fail do
            SEARCH@.ERROR(iter!.G,"ElementNumber");
        od;
        if iter!.G!.FRData.sphere[i]=[] then return fail; fi;
    od;
    return iter!.G!.FRData.sphere[i][n];
end;

SEARCH@.NUMBERELEMENT := function(iter,x)
    local i, n, p;
    i := 1; n := 0;
    repeat
        while not IsBound(iter!.G!.FRData.sphere[i]) and
           SEARCH@.EXTEND(iter!.G,SEARCH@.BALL)=fail do
            SEARCH@.ERROR(iter!.G,"NumberElement");
        od;
        p := Position(iter!.G!.FRData.sphere[i],x);
        if p<>fail then return n+p; fi;
        n := n+Length(iter!.G!.FRData.sphere[i]);
        i := i+1;
    until false;
end;

InstallMethod(Iterator, "(FR) for an FR semigroup",
        [IsFRSemigroup],
        function(G)
    SEARCH@.INIT(G);
    return IteratorByFunctions(rec(
                   NextIterator := SEARCH@.NEXTITERATOR,
                   IsDoneIterator := SEARCH@.ISDONEITERATOR,
                   ShallowCopy := SEARCH@.SHALLOWCOPY,
                   G := G,
                   pos := 0,
                   radius := 0));
end);

InstallMethod(Iterator, "(FR) for an FR semigroup with SC data",
        [IsFRSemigroup and HasFullSCData],
        function(G)
    local maker;
    if DepthOfFRSemigroup(G)<infinity then
        TryNextMethod(); # GAP does a fine job here
    elif IsFinitaryFRSemigroup(G) then
        if IsGroup(G) then
            maker := n->FullSCGroup(AlphabetOfFRSemigroup(G),VertexTransformations(G),n);
        elif IsMonoid(G) then
            maker := n->FullSCMonoid(AlphabetOfFRSemigroup(G),VertexTransformations(G),n);
        else
            maker := n->FullSCSemigroup(AlphabetOfFRSemigroup(G),VertexTransformations(G),n);
        fi;
        return IteratorByFunctions(rec(
                       NextIterator := function(iter)
            local n;
            repeat
                if IsDoneIterator(iter!.iter) then
                    iter!.level := iter!.level+1;
                    iter!.iter := Iterator(maker(iter!.level));
                fi;
                n := NextIterator(iter!.iter);
            until DepthOfFRSemigroup(n)>=iter!.level;
            return n;
        end,
                       IsDoneIterator := ReturnFalse,
                       ShallowCopy := function(iter)
            return rec(NextIterator := iter!.NextIterator,
                       IsDoneIterator := iter!.IsDoneIterator,
                       ShallowCopy := iter!.ShallowCopy,
                       level := iter!.level,
                       iter := ShallowCopy(iter!.iter));
        end,
                       level := 0,
                       iter := Iterator(maker(0))));
    else
        return fail; # probably not worth coding
    fi;
end);

InstallMethod(Enumerator, "(FR) for an FR semigroup",
        [IsFRSemigroup],
        function(G)
    SEARCH@.INIT(G);
    return EnumeratorByFunctions(G,rec(
                   ElementNumber := SEARCH@.ELEMENTNUMBER,
                   NumberElement := SEARCH@.NUMBERELEMENT,
                   G := G));
end);

InstallMethod(PreImagesRepresentativeNC, "(FR) for a map to an FR group",
        [IsGroupGeneralMappingByImages, IsMultiplicativeElementWithInverse],
        function(f,y)
    local iter, x;
    if not IsFRGroup(Range(f)) then TryNextMethod(); fi;
    iter := Iterator(Source(f));
    while not IsDoneIterator(iter) do
        x := NextIterator(iter);
        if x^f=y then return x; fi;
    od;
    return fail;
end);
#############################################################################

#############################################################################
##
#M View
##
BindGlobal("VIEWFRGROUP@", function(G,gens,name)
    local n, s;
    s := "<";
    if HasIsStateClosed(G) then
        if not IsStateClosed(G) then Append(s,"non-"); fi;
        Append(s,"state-closed");
    else
        Append(s,"recursive");
    fi;
    if HasIsRecurrentFRSemigroup(G) and IsRecurrentFRSemigroup(G) then
        Append(s,", recurrent");
    fi;
    if HasIsLevelTransitiveFRGroup(G) and IsLevelTransitiveFRGroup(G) then
        Append(s,", level-transitive");
    fi;
    if HasIsContracting(G) and IsContracting(G) then
        Append(s,", contracting");
    fi;
    if HasIsFinitaryFRSemigroup(G) and IsFinitaryFRSemigroup(G) then
        Append(s,", finitary");
    elif HasIsBoundedFRSemigroup(G) and IsBoundedFRSemigroup(G) then
        Append(s,", bounded");
    elif HasIsPolynomialGrowthFRSemigroup(G) and IsPolynomialGrowthFRSemigroup(G) then
        Append(s,", polynomial-growth");
    elif HasIsFiniteStateFRSemigroup(G) and IsFiniteStateFRSemigroup(G) then
        Append(s,", finite-state");
    fi;
    if HasIsBranched(G) and IsBranched(G) then
        Append(s,", branched");
    fi;
    n := Length(gens(G));
    APPEND@(s," ",name," over ",AlphabetOfFRSemigroup(G)," with ",n," generator");
    if n<>1 then Append(s,"s"); fi;
    if HasSize(G) then APPEND@(s,", of size ",Size(G)); fi;
    Append(s,">");
    return s;
end);

InstallMethod(ViewString, "(FR) for an FR group",
        [IsFRGroup and IsFinitelyGeneratedGroup],
        G->VIEWFRGROUP@(G,GeneratorsOfGroup,"group"));

InstallMethod(ViewString, "(FR) for an FR monoid",
        [IsFRMonoid],
        G->VIEWFRGROUP@(G,GeneratorsOfMonoid,"monoid"));

InstallMethod(ViewString, "(FR) for an FR semigroup",
        [IsFRSemigroup],
        G->VIEWFRGROUP@(G,GeneratorsOfSemigroup,"semigroup"));

INSTALLPRINTERS@(IsFRGroup);
INSTALLPRINTERS@(IsFRMonoid);
INSTALLPRINTERS@(IsFRSemigroup);
#############################################################################

#############################################################################
##
#M ExternalSet
##
InstallOtherMethod(ExternalSet, "(FR) for an FR semigroup and a depth",
        [IsFRSemigroup, IsPosInt],
        function( g, n )
    return ExternalSet(g,Cartesian(List([1..n],i->AlphabetOfFRSemigroup(g))),\^);
end);
#############################################################################

#############################################################################
##
#M VertexTransformations
##
InstallMethod(TopVertexTransformations, "(FR) for a f.g. FR group",
        [IsFRGroup and IsFinitelyGeneratedGroup],
        function(g)
    if GeneratorsOfGroup(g)=[] then return Group(()); fi;
    return Group(List(GeneratorsOfGroup(g),ActivityPerm));
end);

InstallMethod(TopVertexTransformations, "(FR) for a FR monoid",
        [IsFRMonoid],
        function(g)
    if GeneratorsOfMonoid(g)=[] then return Monoid(IdentityTransformation); fi;
    return Monoid(List(GeneratorsOfMonoid(g),ActivityTransformation));
end);

InstallMethod(TopVertexTransformations, "(FR) for a FR semigroup",
        [IsFRSemigroup],
        function(g)
    return Semigroup(List(GeneratorsOfSemigroup(g),ActivityTransformation));
end);

InstallMethod(VertexTransformations, "(FR) for a f.g. FR group",
        [IsFRGroup and IsFinitelyGeneratedGroup],
        function(g)
    if GeneratorsOfGroup(g)=[] then return Group(()); fi;
    return Group(Concatenation(List(GeneratorsOfGroup(g),g->List(States(g),ActivityPerm))));
end);

InstallMethod(VertexTransformations, "(FR) for a FR monoid",
        [IsFRMonoid],
        function(g)
    if GeneratorsOfMonoid(g)=[] then return Monoid(IdentityTransformation); fi;
    return Monoid(Concatenation(List(GeneratorsOfMonoid(g),g->List(States(g),ActivityTransformation))));
end);

InstallMethod(VertexTransformations, "(FR) for a FR semigroup",
        [IsFRSemigroup],
        function(g)
    return Semigroup(Concatenation(List(GeneratorsOfSemigroup(g),g->List(States(g),ActivityTransformation))));
end);

InstallMethod(TopVertexTransformations, "(FR) for a full SC semigroup",
        [IsFRSemigroup and HasFullSCData],
        FullSCVertex);

InstallMethod(VertexTransformations, "(FR) for a full SC semigroup",
        [IsFRSemigroup and HasFullSCData],
        FullSCVertex);
#############################################################################

#############################################################################
##
#M PermGroup
#M EpimorphismPermGroup
##
InstallMethod(PermGroup, "(FR) for a f.g. FR group and a level",
        [IsFRGroup and IsFinitelyGeneratedGroup, IsInt], 1,
        function( g, n )
    if IsTrivial(g) then return Group(()); fi;
    return Group(List(GeneratorsOfGroup(g),x->ActivityPerm(x,n)));
end);

InstallMethod(PermGroup, "(FR) for a full FR group and a level",
        [IsFRGroup and HasFullSCData, IsInt],
        function( g, n )
    return PermGroup(FullSCGroup(FullSCVertex(g),FullSCFilter(g),n),n);
end);

InstallMethod(EpimorphismPermGroup, "(FR) for a f.g. FR group and a level",
        [IsFRGroup, IsInt], 1,
        function( g, n )
    local q, h;
    q := PermGroup(g,n);
    if HasGeneratorsOfGroup(g) then
        h := GroupGeneralMappingByImagesNC(q,g,List(GeneratorsOfGroup(g),w->ActivityPerm(w,n)),GeneratorsOfGroup(g));
        q := GroupHomomorphismByFunction(g,q,w->ActivityPerm(w,n),false,x->ImagesRepresentative(h,x));
    else
        q := GroupHomomorphismByFunction(g,q,w->ActivityPerm(w,n));
    fi;
    SetLevelOfEpimorphismFromFRGroup(q,n);
    return q;
end);

InstallMethod(EpimorphismPermGroup, "(FR) for a full FR group and a level",
        [IsFRGroup and HasFullSCData, IsInt],
        function( g, n )
    local gn, q, h;
    if DepthOfFRSemigroup(g)>n then
        gn := FullSCGroup(FullSCVertex(g),FullSCFilter(g),n);
    else
        gn := g;
    fi;
    q := PermGroup(gn,n);
    h := GroupGeneralMappingByImagesNC(q,g,List(GeneratorsOfGroup(gn),w->ActivityPerm(w,n)),GeneratorsOfGroup(gn));
    q := GroupHomomorphismByFunction(g,q,w->ActivityPerm(w,n),false,x->ImagesRepresentative(h,x));
    SetLevelOfEpimorphismFromFRGroup(q,n);
    return q;
end);

BindGlobal("PERMTRANS2COLL@", function(l)
    local i;
    if IsCollection(l) then
        return l;
    else # is a combination of permutations and transformations
        for i in [1..Length(l)] do
            if IsPerm(l[i]) then l[i] := AsTransformation(l[i]); fi;
        od;
        return l;
    fi;
end);

BindGlobal("TRANSMONOID@", function(g,n,gens,filt,fullconstr,mconstr,constr,subconstr,activity)
    local s;
    if ForAny(filt,x->x(g)) then
        s := gens(g);
    elif HasFullSCData(g) then
        s := gens(fullconstr(FullSCVertex(g),FullSCFilter(g),n));
    else
        TryNextMethod();
    fi;
    if s=[] then # GAP hates monoids and semigroups with 0 generators
        return subconstr(mconstr(constr([1..Size(AlphabetOfFRSemigroup(g))^n])),[]);
    fi;
    return mconstr(PERMTRANS2COLL@(List(s,x->activity(x,n))));
end);

InstallMethod(TransformationMonoid, "(FR) for a f.g. FR monoid and a level",
        [IsFRMonoid, IsInt],
        function(g, n)
    return TRANSMONOID@(g,n,GeneratorsOfMonoid,[HasGeneratorsOfMonoid,HasGeneratorsOfGroup],FullSCMonoid,Monoid,Transformation,Submonoid,ActivityTransformation);
end);

InstallMethod(EpimorphismTransformationMonoid, "(FR) for a f.g. FR monoid and a level",
        [IsFRMonoid, IsInt],
        function(g, n)
    local q, f;
    q := TransformationMonoid(g,n);
    f := MagmaHomomorphismByFunctionNC(g,q,w->ActivityTransformation(w,n));
    f!.prefun := x->Error("Factorization not implemented in monoids");
    return f;
end);

InstallMethod(TransformationSemigroup, "(FR) for a f.g. FR semigroup and a level",
        [IsFRSemigroup, IsInt],
        function(g, n)
    return TRANSMONOID@(g,n,GeneratorsOfSemigroup,[HasGeneratorsOfSemigroup,HasGeneratorsOfMonoid,HasGeneratorsOfGroup],FullSCSemigroup,Semigroup,Transformation,Subsemigroup,ActivityTransformation);
end);

InstallMethod(EpimorphismTransformationSemigroup, "(FR) for a f.g. FR semigroup and a level",
        [IsFRSemigroup, IsInt],
        function( g, n )
    local q ,f;
    q := TransformationSemigroup(g,n);
    f := MagmaHomomorphismByFunctionNC(g,q,w->ActivityTransformation(w,n));
    f!.prefun := x->Error("Factorization not implemented in semigroups");
    return f;
end);

InstallMethod(PcGroup, "(FR) for an FR group and a level",
        [IsFRGroup, IsInt],
        function(g,n)
    local q;
    q := Image(IsomorphismPcGroup(PermGroup(g,n)));
    if IsPGroup(VertexTransformations(g)) then
        SetPrimePGroup(q,PrimePGroup(VertexTransformations(g)));
    fi;
    return q;
end);

InstallMethod(EpimorphismPcGroup, "(FR) for an FR group and a level",
        [IsFRGroup, IsInt],
        function(g,n)
    local q;
    q := EpimorphismPermGroup(g,n);
    q := q*IsomorphismPcGroup(Image(q));
    if IsPGroup(VertexTransformations(g)) then
        SetPrimePGroup(Range(q),PrimePGroup(VertexTransformations(g)));
    fi;
    return q;
end);

InstallMethod(KernelOfMultiplicativeGeneralMapping, "(FR) for an epimorphism to perm or pc group",
        [IsGroupHomomorphism and HasLevelOfEpimorphismFromFRGroup],
        f->LevelStabilizer(Source(f),LevelOfEpimorphismFromFRGroup(f)));
#############################################################################

#############################################################################
##
#P IsContracting
#A NucleusOfFRSemigroup
#A NucleusMachine
##
InstallMethod(IsContracting, "(FR) for an FR semigroup",
        [IsFRSemigroup],
        function(G)
    local N;
    N := NucleusOfFRSemigroup(G);
    return IsCollection(N) and IsFinite(N);
end);

InstallMethod(NucleusOfFRSemigroup, "(FR) for an FR semigroup",
        [IsFRSemigroup],
        G->NUCLEUS@(GeneratorsOfSemigroup(G)));

InstallMethod(NucleusMachine, "(FR) for an FR semigroup",
        [IsFRSemigroup],
        G->AsMealyMachine(NucleusOfFRSemigroup(G)));

BindGlobal("ADJACENCYBASESWITHONE@",
        function(nuke)
    local seen, i, j, a, len, u, bases, basepos, machine, skip, addelt;
    
    addelt := function(new)
        local i;
        i := 1;
        while i <= Length(bases) do
            if IsSubset(bases[i],new) then
                return false;
            elif IsSubset(new,bases[i]) then
                Remove(bases,i);
                if basepos >= i then basepos := basepos-1; fi;
            else
                i := i+1;
            fi;
        od;
        Add(bases,new);
        return true;
    end;
    
    nuke := Set(nuke);
    machine := AsMealyMachine(nuke);
    
    seen := [[[1..Length(nuke)],[],false]];
    bases := [];
    len := 1;
    basepos := 1;
    while len <= Length(seen) do
        if seen[len][3] then len := len+1; continue; fi;
        for i in AlphabetOfFRObject(machine) do
            u := Set(seen[len][1],x->Transition(machine,x,i));
            a := Concatenation(seen[len][2],[i]);
            skip := false;
            for j in [1..Length(seen)] do
                if a{[1..Length(seen[j][2])]}=seen[j][2] then # parent
                    if seen[j][1]=u then
                        addelt(u);
                        skip := true;
                        break;
                    fi;
                elif j > len then
                    if IsSubset(seen[j][1],u) then skip := true; break; fi;
                fi;
            od;
            Add(seen,[u,a,skip]);
        od;
        len := len+1;
    od;
    
    basepos := 1;
    while basepos <= Length(bases) do
        for i in AlphabetOfFRObject(machine) do
            addelt(Set(bases[basepos],x->Transition(machine,x,i)));
        od;
        basepos := basepos+1;
    od;
    return [bases,nuke,List(bases,x->nuke{x})];
end);

InstallMethod(AdjacencyBasesWithOne, "(FR) for a nucleus",
        [IsFRElementCollection],
        L->ADJACENCYBASESWITHONE@(L)[3]);

InstallMethod(AdjacencyBasesWithOne, "(FR) for an FR semigroup",
        [IsFRSemigroup],
        G->ADJACENCYBASESWITHONE@(NucleusOfFRSemigroup(G))[3]);

BindGlobal("ADJACENCYPOSET@",
        function(nuke)
    local b, c, x, y, elements, oldelements, rel, bases, dom;
    
    bases := ADJACENCYBASESWITHONE@(nuke);
    nuke := bases[2];
    bases := bases[1];
    
    elements := [];
    for b in bases do
#        for x in b do # that would be to include adjacent tiles in the relation
#            c := b "/" nuke[x];
            c := b;
            if not c in elements then
                oldelements := ShallowCopy(elements);
                AddSet(elements,c);
                for y in oldelements do
                    AddSet(elements,Intersection(y,c));
                od;
            fi;
#        od;
    od;
    dom := Domain(List(elements,x->nuke{x}));
    rel := [];
    for b in elements do for c in elements do
        if IsSubset(b,c) then Add(rel,DirectProductElement([nuke{b},nuke{c}])); fi;
    od; od;
    return BinaryRelationByElements(dom,rel);
end);

InstallMethod(AdjacencyPoset, "(FR) for a nucleus",
        [IsFRElementCollection],
        ADJACENCYPOSET@);

InstallMethod(AdjacencyPoset, "(FR) for an FR semigroup",
        [IsFRSemigroup],
        G->ADJACENCYPOSET@(NucleusOfFRSemigroup(G)));
#############################################################################

#############################################################################
##
#M  Degree
##
BindGlobal("FILTERCOMPARE@", function(G,filter)
    if FullSCFilter(G)=IsFRObject then
        TryNextMethod();
    fi;
    return Position(FILTERORDER@,FullSCFilter(G))<=Position(FILTERORDER@,filter);
end);

InstallMethod(DegreeOfFRSemigroup, "(FR) for a self-similar semigroup",
        [IsFRSemigroup],
        G->Maximum(List(GeneratorsOfSemigroup(G),DegreeOfFRElement)));
InstallMethod(Degree, [IsFRSemigroup], DegreeOfFRSemigroup);
InstallMethod(DegreeOfFRSemigroup, "(FR) for a full SC semigroup",
        [IsFRSemigroup and HasFullSCData],
        function(G)
    if IsTrivial(G) then
        return -1;
    elif IsFinitaryFRSemigroup(G) then
        return 0;
    elif IsBoundedFRSemigroup(G) then
        return 1;
    else
        return infinity;
    fi;
end);

InstallMethod(IsFinitaryFRSemigroup, "(FR) for a self-similar semigroup",
        [IsFRSemigroup],
        G->ForAll(GeneratorsOfSemigroup(G),IsFinitaryFRElement));
InstallMethod(IsFinitaryFRSemigroup, "(FR) for a full SC semigroup",
        [IsFRSemigroup and HasFullSCData],
        G->FILTERCOMPARE@(G,IsFinitaryFRElement));

InstallMethod(IsWeaklyFinitaryFRSemigroup, "(FR) for a self-similar semigroup",
        [IsFRSemigroup],
        G->ForAll(GeneratorsOfSemigroup(G),IsWeaklyFinitaryFRElement));
InstallTrueMethod(IsFinitaryFRSemigroup, IsWeaklyFinitaryFRSemigroup);

InstallMethod(DepthOfFRSemigroup, "(FR) for a self-similar semigroup",
        [IsFRSemigroup],
        G->Maximum(List(GeneratorsOfSemigroup(G),DepthOfFRElement)));
InstallMethod(Depth, [IsFRSemigroup], DepthOfFRSemigroup);

InstallMethod(IsBoundedFRSemigroup, "(FR) for a self-similar semigroup",
        [IsFRSemigroup],
        G->ForAll(GeneratorsOfSemigroup(G),IsBoundedFRElement));
InstallMethod(IsBoundedFRSemigroup, "(FR) for a full SC semigroup",
        [IsFRSemigroup and HasFullSCData],
        G->FILTERCOMPARE@(G,IsBoundedFRElement));

InstallMethod(IsPolynomialGrowthFRSemigroup, "(FR) for a self-similar semigroup",
        [IsFRSemigroup],
        G->ForAll(GeneratorsOfSemigroup(G),IsPolynomialGrowthFRElement));
InstallMethod(IsPolynomialGrowthFRSemigroup, "(FR) for a full SC semigroup",
        [IsFRSemigroup and HasFullSCData],
        G->FILTERCOMPARE@(G,IsPolynomialGrowthFRElement));

InstallMethod(IsFiniteStateFRSemigroup, "(FR) for a self-similar semigroup",
        [IsFRSemigroup],
        G->ForAll(GeneratorsOfSemigroup(G),IsFiniteStateFRElement));
InstallMethod(IsFiniteStateFRSemigroup, "(FR) for a full SC semigroup",
        [IsFRSemigroup and HasFullSCData],
        G->FILTERCOMPARE@(G,IsFiniteStateFRElement));
#############################################################################

#############################################################################
##
#M  IsTorsionGroup
#M  IsTorsionFreeGroup
#M  IsAmenableGroup
#M  IsVirtuallySimpleGroup
#M  IsSQUniversal
#M  IsResiduallyFinite
#M  IsJustInfinite
##
InstallTrueMethod(IsResiduallyFinite, IsFinite);
InstallTrueMethod(IsResiduallyFinite, IsFreeGroup);
InstallMethod(IsResiduallyFinite, "(FR) for an FR group",
        [IsFRGroup],
        function(G)
    return IsFinite(AlphabetOfFRSemigroup(G)) or IsResiduallyFinite(VertexTransformations(G));
end);

InstallMethod(IsJustInfinite, "(FR) for a free group",
        [IsFreeGroup],
        G->RankOfFreeGroup(G)=1);
InstallMethod(IsJustInfinite, "(FR) for a finite group",
        [IsGroup and IsFinite],
        ReturnFalse);
InstallMethod(IsJustInfinite, "(FR) for a f.p. group",
        [IsFpGroup],
        function(G)
    if 0 in AbelianInvariants(G) and not IsCyclic(G) then
        return false;
    fi;
    if IsFinite(G) then
        return false;
    fi;
    TryNextMethod();
end);
InstallMethod(IsJustInfinite, "(FR) for a FR group",
        [IsFRGroup],
        function(G)
    local K;
    K := BranchingSubgroup(G);
    if K=fail then
        TryNextMethod();
    fi;
    return Index(G,K)<infinity and not 0 in AbelianInvariants(K);
end);

InstallMethod(IsSQUniversal, "(FR) for a free group",
        [IsFreeGroup],
        G->RankOfFreeGroup(G)>=2);
InstallMethod(IsSQUniversal, "(FR) for a finite object",
        [IsFinite],
        ReturnFalse);
InstallMethod(IsSQUniversal, "(FR) for an amenable group",
        [IsGroup],
        function(G)
    if HasIsAmenableGroup(G) and IsAmenableGroup(G) then
        return false;
    fi;
    TryNextMethod();
end);

BindGlobal("TORSIONSTATES@", function(g)
    local s, todo, i, j, x, y;
    todo := [g];
    i := 1;
    while i <= Length(todo) do
        if Order(todo[i])=infinity then
            return fail;
        fi;
        x := DecompositionOfFRElement(todo[i]);
        for j in Cycles(PermList(x[2]),AlphabetOfFRObject(g)) do
            y := Product(x[1]{j});
            if not y in todo then
                Add(todo,y);
            fi;
        od;
        i := i+1;
    od;
    return todo;
end);

BindGlobal("TORSIONLIMITSTATES@", function(L)
    local s, d, dd, S, oldS, i, x;
    s := [];
    for i in L do
        x := TORSIONSTATES@(i);
        if x=fail then return fail; fi;
        UniteSet(s,x);
    od;
    d := [];
    for i in s do
        x := DecompositionOfFRElement(i);
        dd := [];
        for i in Cycles(PermList(x[2]),AlphabetOfFRObject(L[1])) do
            Add(dd,Position(s,Product(x[1]{i})));
        od;
        Add(d,dd);
    od;
    S := [1..Length(s)];
    repeat
        oldS := S;
        S := Union(d{S});
    until oldS=S;
    return Set(s{S});
end);

BindGlobal("TORSIONNUCLEUS@", function(G)
    local s, olds, news, gens, i, j;

    gens := Set(GeneratorsOfSemigroup(G));
    s := TORSIONLIMITSTATES@(gens);
    if s=fail then return fail; fi;
    olds := [];
    repeat
        news := Difference(s,olds);
        olds := ShallowCopy(s);
        for i in news do
            for j in gens do AddSet(s,i*j); od;
        od;
        s := TORSIONLIMITSTATES@(s);
        if s=fail then return fail; fi;
        Info(InfoFR, 2, "TorsionNucleus: The nucleus contains at least ",s);
    until olds=s;
    return s;
end);

InstallTrueMethod(IsTorsionGroup, IsGroup and IsFinite);

InstallMethod(IsTorsionGroup, "(FR) for a self-similar group",
        [IsFRGroup],
        function(G)
    Info(InfoFR,1,"Beware! This code has not been tested nor proven valid!");
    return TORSIONNUCLEUS@(G)<>fail;
end);

InstallMethod(IsTorsionFreeGroup, "(FR) for a self-similar group",
        [IsFRGroup],
        function(G)
    local iter, n, g;
    if IsBoundedFRSemigroup(G) and ForAll(AbelianInvariants(G),IsZero)
       and IsAbelian(VertexTransformations(G)) then
        return true;
    fi;
    iter := Iterator(G);
    n := 0;
    # !!! have to be much more subtle!!! is there a finite set of
    # infinite-order elements such that for all elements x in G,
    # a stable state of some x^n belongs to the set?
    while true do
        g := NextIterator(iter);
        if IsOne(g) then continue; fi;
        if Order(g)<infinity then return false; fi;
        n := n+1;
        if RemInt(n,100)=0 then
            Info(InfoFR,2,"IsTorsionFreeGroup: size is at least ",n);
        fi;
    od;
end);

InstallTrueMethod(IsTorsionFreeGroup, IsFreeGroup);

InstallMethod(IsSolvableGroup, [IsFreeGroup and IsFinitelyGeneratedGroup],
        function(G)
    return RankOfFreeGroup(G)<=1;
end);

InstallTrueMethod(IsAmenableGroup, IsGroup and IsBoundedFRSemigroup);
InstallTrueMethod(IsAmenableGroup, IsGroup and IsFinite);
InstallTrueMethod(IsAmenableGroup, IsSolvableGroup);

InstallMethod(IsAmenableGroup, [IsFreeGroup],
        G->RankOfFreeGroup(G)<=1);
#!!! could be much more subtle, e.g. handling small cancellation groups
#############################################################################

#############################################################################
##
#F  FRGroup
#F  FRSemigroup
#F  FRMonoid
##
BindGlobal("STRING_ATOM2GAP@", function(s)
    local stream, result;
    stream := "return ";
    Append(stream,s);
    Append(stream,";");
    stream := InputTextString(stream);
    result := ReadAsFunction(stream)();
    CloseStream(stream);
    return result;
end);
# gens: list of strings corresponding to generator names
# imgs: a list of the same length as imgs
# w: a string containing an expression in terms of the generators names in `gens`
BindGlobal("STRING_WORD2GAP@", function(gens,imgs,w)
    local s, f, i, argname;
    # generate an identifier that is definitely not in gens by making it
    # longer than any element of gens
    argname := ListWithIdenticalEntries(Maximum(List(gens, Length))+1, '_');
--> --------------------

--> maximum size reached

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

[ Dauer der Verarbeitung: 0.135 Sekunden  ]