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

SSL recognition.gi   Sprache: unbekannt

 
#############################################################################
##
##  This file is part of recog, a package for the GAP computer algebra system
##  which provides a collection of methods for the constructive recognition
##  of groups.
##
##  This files's authors include Max Neunhöffer, Ákos Seress.
##
##  Copyright of recog belongs to its developers whose names are too numerous
##  to list here. Please refer to the COPYRIGHT file for details.
##
##  SPDX-License-Identifier: GPL-3.0-or-later
##
##
##  The generic code for recognition, implementation part.
##
#############################################################################

BindGlobal("RECOG_FindKernelFastNormalClosureStandardArgumentValues",
           Immutable([6, 3]));
BindConstant("RECOG_NrElementsInImmediateVerification", 10);

# a nice view method:
RECOG_ViewObj := function( level, ri )
    local ms;
    if IsReady(ri) then
        Print("<recognition node ");
    else
        Print("<failed recognition node ");
    fi;
    if IsBound(ri!.projective) and ri!.projective then
        Print("(projective) ");
    fi;
    if Hasfhmethsel(ri) then
        ms := fhmethsel(ri);
        if IsRecord(ms) then
            if IsBound(ms.successMethod) then
                Print(ms.successMethod);
            else
                Print("NO STAMP");
            fi;
        elif IsString(ms) then
            Print(ms);
        fi;
        if IsBound(ri!.comment) then
            Print(" Comment=", ri!.comment);
        fi;
    fi;
    if HasIsRecogInfoForSimpleGroup(ri) and IsRecogInfoForSimpleGroup(ri) then
        Print(" Simple");
    elif HasIsRecogInfoForAlmostSimpleGroup(ri) and IsRecogInfoForAlmostSimpleGroup(ri) then
        Print(" AlmostSimple");
    fi;
    if HasSize(ri) then
        Print(" Size=",Size(ri));
    fi;
    if HasGrp(ri) and IsMatrixGroup(Grp(ri)) then
        Print(" Dim=",ri!.dimension);
        Print(" Field=",Size(ri!.field));
    fi;
    if not IsLeaf(ri) then
        Print("\n",String("",level)," F:");
        if HasImageRecogNode(ri) then
            RECOG_ViewObj(level+3, ImageRecogNode(ri));
        else
            Print("has no image");
        fi;
        Print("\n",String("",level), " K:");
        if HasKernelRecogNode(ri) then
            if KernelRecogNode(ri) = fail then
                Print("<trivial kernel");
            else
                RECOG_ViewObj(level+3, KernelRecogNode(ri));
            fi;
        else
            Print("has no kernel");
        fi;
    fi;
    Print(">");
  end;

InstallMethod( ViewObj, "for recognition nodes", [IsRecogNode],
  function(ri)
    RECOG_ViewObj(0, ri);
  end);


#############################################################################
# The main recursive function:
#############################################################################

InstallGlobalFunction( RecognisePermGroup,
  function(G)
    return RecogniseGeneric(G, FindHomDbPerm, "", rec());
  end);

InstallGlobalFunction( RecogniseMatrixGroup,
  function(G)
    return RecogniseGeneric(G, FindHomDbMatrix, "", rec());
  end);

InstallGlobalFunction( RecogniseProjectiveGroup,
  function(G)
    return RecogniseGeneric(G, FindHomDbProjective, "", rec());
  end);

InstallGlobalFunction( RecogniseGroup,
  function(G)
    if IsPermGroup(G) then
        return RecogniseGeneric(G, FindHomDbPerm, "", rec());
    elif IsMatrixGroup(G) then
        return RecogniseGeneric(G, FindHomDbMatrix, "", rec());
    else
        ErrorNoReturn("Only matrix and permutation groups are supported");
    fi;

# TODO: perhaps check if the result does not have IsReady set;
# in that case print a warning or error or ...?

    # Note: one cannot use "RecogniseGroup" to recognise projective groups
    #       as of now since "Projective groups" do not yet exist as GAP
    #       objects here!
  end);

# TODO: TryFindHomMethod is never called by anything
# in recog.
# Seems to be for user intervention and/or debugging?
# If so, document it accordingly. Otherwise remove it!
InstallGlobalFunction( TryFindHomMethod,
  function( g, method, projective )
    local result,ri;
    ri := RecogNode(g,projective);
    Unbind(g!.pseudorandomfunc);
    result := method(ri,g);
    if result in [TemporaryFailure, NeverApplicable] then
        return result;
    else
        SetFilterObj(ri,IsReady);
        Setfhmethsel(ri,"User");
        return ri;
    fi;
  end );

InstallMethod( RecogNode,
  "standard method",
  [ IsGroup, IsBool, IsRecord ],
  function(H,projective,r)
    local ri;
    ri := ShallowCopy(r);
    Objectify( RecogNodeType, ri );
    SetGrp(ri,H);
    Setslpforelement(ri,SLPforElementGeneric);
    SetgensN(ri,[]);       # this will grow over time
    Setimmediateverification(ri,false);
    SetInitialDataForKernelRecogNode(ri,rec(hints := []));
          # this is eventually handed down to the kernel
    SetInitialDataForImageRecogNode(ri,rec(hints := []));
          # this is eventually handed down to the image
    if projective then
        Setisone(ri,IsOneProjective);
        Setisequal(ri,IsEqualProjective);
        SetOrderFunc(ri, RECOG.ProjectiveOrder);
    else
        Setisone(ri,IsOne);
        Setisequal(ri,\=);
        SetOrderFunc(ri, Order);
    fi;
    Setdocommute(ri, function(x,y)
      local a,b;
      a := StripMemory(x);
      b := StripMemory(y);
      return isequal(ri)(a*b, b*a);
    end);
    ri!.projective := projective;
    # FIXME: perhaps DON'T set a default for findgensNmeth, to ensure
    # people always set a value? Or at least find some way so that
    # methods must explicitly acknowledge that they want the default...
    SetfindgensNmeth(
        ri,
        rec(method := FindKernelFastNormalClosure,
            args := ShallowCopy(RECOG_FindKernelFastNormalClosureStandardArgumentValues))
    );
    if IsMatrixGroup(H) then
        ri!.field := FieldOfMatrixGroup(H);
        ri!.dimension := DimensionOfMatrixGroup(H);
    fi;
    ri!.pr := ProductReplacer(GeneratorsOfGroup(H));
    ri!.gensHmem := GeneratorsWithMemory(GeneratorsOfGroup(H));
    ri!.prodrep := ProductReplacer(ri!.gensHmem, rec( maxdepth := 400 ));
    # The purpose of the following components is to use the components randr
    # and rando as the central places where random elements and their orders
    # can be stored.
    #
    # randr stores the random elements computed by RandomElm and RandomElmOrd
    ri!.randr := EmptyPlist(100);
    # If the order of a random element is computed by RandomElmOrd or if the
    # order of an entry of randr is computed by GetElmOrd, then the order is
    # saved into rando.
    ri!.rando := EmptyPlist(100);
    # randrpt stores for each stamp how often it was used to generate a random
    # element.
    ri!.randrpt := rec();
    # randopt stores for each stamp how often it was used to generate a random
    # order.
    ri!.randopt := rec();
    ri!.randstore := true;
    # randp and randppt were used to store ppd elements. Currently unused.
    #ri!.randp := EmptyPlist(100);
    #ri!.randppt := rec();
    H!.pseudorandomfunc := [rec(func := function(ri,name,bool)
                                          return RandomElm(ri,name,bool).el;
                                        end,
                                args := [ri,"PseudoRandom",false])];
    return ri;
  end );

InstallOtherMethod( RecogNode,
  "for a group",
  [ IsGroup ],
  function(H)
    return RecogNode(H, false, rec());
  end );

InstallOtherMethod( RecogNode,
  "for a group and a boolean",
  [ IsGroup, IsBool ],
  function(H, projective)
    return RecogNode(H, projective, rec());
  end );

# Sets the stamp used by RandomElm, RandomElmOrd, and related functions.
RECOG.SetPseudoRandomStamp := function(g,st)
  if IsBound(g!.pseudorandomfunc) then
      g!.pseudorandomfunc[Length(g!.pseudorandomfunc)].args[2] := st;
  fi;
end;

# RandomElm and RandomElmOrd take a recog record, a string, and a
# bool as inputs.
# The string is used as a stamp to request a random element or order for a
# specific computation. RandomElm and RandomElmOrd will first try to reuse
# random elements and orders generated with different stamps.
# For example, if a computation which used stamp := "A" has already computed
# random elements or orders, then RandomElm and RandomElmOrd will reuse these
# if called with stamp := "B".
#
# The components of the recog record involved are explained in
# RecogNode.
#
# HACK: For recog records created by RecogNode the method
# RandomElm is by default stored in the component ri!.Grp!.pseudorandomfunc.
# A method for PseudoRandom is installed such that it calls
# RandomElm(ri, "PseudoRandom", false).
InstallMethod( RandomElm, "for a recognition node, a string and a bool",
  [ IsRecogNode, IsString, IsBool ],
  function(ri, stamp, mem)
    local pos, el, res;
    if ri!.randstore then
        if IsBound(ri!.randrpt.(stamp)) then
            ri!.randrpt.(stamp) := ri!.randrpt.(stamp) + 1;
        else
            ri!.randrpt.(stamp) := 1;
        fi;
        pos := ri!.randrpt.(stamp);
        if not IsBound(ri!.randr[pos]) then
            ri!.randr[pos] := Next(ri!.prodrep);
        fi;
        el := ri!.randr[pos];
    else
        el := Next(ri!.prodrep);
    fi;
    res := rec();
    if mem then
        res.el := el;
    else
        res.el := el!.el;
    fi;
    if ri!.randstore then
        res.nr := pos;
    fi;
    return res;
  end );

# For an explanation see RandomElm.
InstallMethod( RandomElmOrd,
  "for a recognition node, a string and a bool",
  [ IsRecogNode, IsString, IsBool ],
  function(ri, stamp, mem)
    local pos,res;
    if ri!.randstore then
        if IsBound(ri!.randopt.(stamp)) then
            ri!.randopt.(stamp) := ri!.randopt.(stamp) + 1;
        else
            ri!.randopt.(stamp) := 1;
        fi;
        pos := ri!.randopt.(stamp);
        if not IsBound(ri!.rando[pos]) then
            if not IsBound(ri!.randr[pos]) then
                ri!.randr[pos] := Next(ri!.prodrep);
            fi;
            ri!.rando[pos] := OrderFunc(ri)(ri!.randr[pos]!.el);
        fi;
        res := rec( order := ri!.rando[pos], projective := ri!.projective,
                    el := ri!.randr[pos] );
    else
        res := rec( el := Next(ri!.prodrep) );
        res.order := OrderFunc(ri)(res.el!.el);
        res.projective := ri!.projective;
        Add(ri!.rando,res.order);
    fi;
    if not mem then
        res.el := res.el!.el;
    fi;
    return res;
  end );

# GetElmOrd takes a recognition node and a record r. The record r
# typically is created by RandomElm and represents a random element of Grp(ri).
# If ri!.randstore is true this function tries to look up or computes and
# stores the order in ri.
InstallMethod( GetElmOrd, "for a recognition node and a record",
  [ IsRecogNode, IsRecord ],
  function( ri, r )
    local x;
    if ri!.randstore then
        if IsBound(ri!.rando[r.nr]) then
            r.order := ri!.rando[r.nr];
        else
            ri!.rando[r.nr] := OrderFunc(ri)(ri!.randr[r.nr]!.el);
            r.order := ri!.rando[r.nr];
        fi;
    else
        x := StripMemory(r.el);
        r.order := OrderFunc(ri)(x);
    fi;
  end );

# FIXME: unused?
# InstallMethod( RandomElmPpd,
#   "for a recognition node, a string and a bool",
#   [ IsRecogNode, IsString, IsBool ],
#   function(ri, s, mem)
#     local pos,res;
#     if ri!.randstore then
#         if not IsBound(ri!.randppt.(s)) then
#             ri!.randppt.(s) := 1;
#             pos := 1;
#         else
#             ri!.randppt.(s) := ri!.randppt.(s) + 1;
#             pos := ri!.randppt.(s);
#         fi;
#         if not IsBound(ri!.randp[pos]) then
#             if not IsBound(ri!.randr[pos]) then
#                 ri!.randr[pos] := Next(ri!.prodrep);
#             fi;
#             if not IsMatrix(ri!.randr[pos]) then
#                 ErrorNoReturn("ppd elements only make sense for matrices");
#             fi;
#             res := rec( el := ri!.randr[pos] );
#             res.charpoly := CharacteristicPolynomial(ri!.field,ri!.field,
#                                                      res.el!.el,1);
#             res.factors := Factors(PolynomialRing(ri!.field), res.charpoly);
#             res.degrees := List(res.factors,Degree);
#             res.degset := Set(res.degrees);
#             ri!.randp[pos] := ShallowCopy(res);
#             Unbind(ri!.randp[pos].el);
#         else
#             res := ShallowCopy(ri!.randp[pos]);
#             res.el := ri!.randr[pos];
#         fi;
#     else
#         res := rec( el := Next(ri!.prodrep) );
#         res.charpoly := CharacteristicPolynomial(ri!.field,ri!.field,
#                                                  res.el!.el,1);
#         res.factors := Factors(PolynomialRing(ri!.field), res.charpoly);
#         res.degrees := List(res.factors,Degree);
#         res.degset := Set(res.degrees);
#     fi;
#     if not mem then
#         res.el := res.el!.el;
#     fi;
#     return res;
#   end );

# FIXME: unused?
# InstallMethod( GetElmPpd, "for a recognition node and a record",
#   [ IsRecogNode, IsRecord ],
#   function( ri, r )
#     local x;
#     if IsObjWithMemory(r.el) then
#         x := r.el!.el;
#     else
#         x := r.el;
#     fi;
#     if ri!.randstore and r.nr > 0 then
#         if not IsBound(ri!.randp[r.nr]) then
#             r.charpoly := CharacteristicPolynomial(ri!.field,ri!.field,x,1);
#             r.factors := Factors(PolynomialRing(ri!.field), r.charpoly);
#             r.degrees := List(r.factors,Degree);
#             r.degset := Set(r.degrees);
#             ri!.randp[r.nr] := ShallowCopy(r);
#             Unbind(ri!.randp[r.nr].el);
#             Unbind(ri!.randp[r.nr].nr);
#         else
#             r.charpoly := ri!.randp[r.nr].charpoly;
#             r.factors := ri!.randp[r.nr].factors;
#             r.degrees := ri!.randp[r.nr].degrees;
#             r.degset := ri!.randp[r.nr].degset;
#         fi;
#     else
#         r.charpoly := CharacteristicPolynomial(ri!.field,ri!.field,x,1);
#         r.factors := Factors(PolynomialRing(ri!.field), r.charpoly);
#         r.degrees := List(r.factors,Degree);
#         r.degset := Set(r.degrees);
#     fi;
#   end );

InstallMethod( RandomOrdersSeen, "for a recognition node",
  [ IsRecogNode ],
  function(ri)
    return Compacted(ri!.rando);
  end );

# FIXME: unused?
# InstallMethod( StopStoringRandEls, "for a recognition node",
#   [ IsRecogNode ],
#   function(ri)
#     ri!.randstore := false;
#     Unbind(ri!.randr);
#     Unbind(ri!.randp);
#     Unbind(ri!.randrpt);
#     Unbind(ri!.randopt);
#     Unbind(ri!.randppt);
#     ri!.rando := Compacted(ri!.rando);
#     # Note that we keep the random element orders seen!
#   end );

InstallGlobalFunction( PrintTreePos,
  function(mark,depthString,H)
    if InfoLevel(InfoRecog) = 1 then
        if IsMatrixGroup(H) then
            Print(mark," dim=",String(DimensionOfMatrixGroup(H),4),
                  " field=",Size(FieldOfMatrixGroup(H))," ",
                  String(Length(depthString),2)," ",depthString,"   \r");
        elif IsPermGroup(H) then
            Print(mark," pts=",String(LargestMovedPoint(H),6)," ",
                  String(Length(depthString),2)," ",depthString,"   \r");
        else
            Print(mark," ",String(Length(depthString),2)," ",depthString,"   \r");
        fi;
    fi;
  end );

InstallGlobalFunction( RecogniseGeneric,
  function(H, methoddb, depthString, knowledge)
    # Assume all the generators have no memory!
    local N,depth,done,i,l,ll,allmethods,
          hint,
          proj1,proj2,ri,rifac,riker,s,x,y,z,succ,counter;

    depth := Length(depthString);

    PrintTreePos("E",depthString,H);
    Info(InfoRecog,4,"Recognising: ",H);

    if Length(GeneratorsOfGroup(H)) = 0 then
        H := Group([One(H)]);
    fi;

    # Set up the record and the group object:
    ri := RecogNode(
        H,
        IsIdenticalObj( methoddb, FindHomDbProjective ),
        knowledge
    );
    # was here earlier: Setcalcnicegens(ri,CalcNiceGensGeneric);
    Setmethodsforimage(ri,methoddb);

    # Combine database of find homomorphism methods with hints
    allmethods := methoddb;
    if IsBound(knowledge.hints) then
        allmethods := Concatenation(allmethods, knowledge.hints);
        SortBy(allmethods, a -> -a.rank);
    fi;
    # verify no rank occurs more than once
    Assert(0, Length(Set(allmethods, m->m.rank)) = Length(allmethods));

    # Find a possible homomorphism (or recognise this group as leaf)
    Setfhmethsel(ri, CallMethods( allmethods, 10, ri, H ));
    # TODO: extract the value 10 into a named constant, and / or make it
    #       an option parameter to the func

    # Reset the pseudo random stamp:
    RECOG.SetPseudoRandomStamp(Grp(ri),"PseudoRandom");

    # Handle the unfinished case:
    if fhmethsel(ri).result = TemporaryFailure then
        SetFilterObj(ri,IsLeaf);
        if InfoLevel(InfoRecog) = 1 and depth = 0 then Print("\n"); fi;
        Info(InfoRecog, 1,
             "RecogNode <ri> could not be recognised,",
             " IsReady(<ri>) is not set, recognition aborts");
        return ri;
    fi;

    # Handle the leaf case:
    if IsLeaf(ri) then
        # If nobody has set how we produce preimages of the nicegens:
        if not Hascalcnicegens(ri) then
            Setcalcnicegens(ri,CalcNiceGensGeneric);
        fi;
        if Hasslptonice(ri) then
            SlotUsagePattern(slptonice(ri));
        fi;
        # Handle the case that nobody set nice generators:
        if not HasNiceGens(ri) then
            if Hasslptonice(ri) then
                SetNiceGens(ri,ResultOfStraightLineProgram(slptonice(ri),
                                            GeneratorsOfGroup(H)));
            else
                # FIXME: is this a good idea???
                # maybe an error would be better for debugging
                SetNiceGens(ri,GeneratorsOfGroup(H));
            fi;
        fi;
        if InfoLevel(InfoRecog) = 1 and depth = 0 then Print("\n"); fi;
        # StopStoringRandEls(ri);
        SetFilterObj(ri,IsReady);
        return ri;
    fi;

    # The non-leaf case:
    # In that case we know that ri now knows: homom plus additional data.

    # Try to recognise the image a few times, then give up:
    counter := 0;
    repeat
        counter := counter + 1;
        if counter > 10 then
            if InfoLevel(InfoRecog) = 1 and depth = 0 then Print("\n"); fi;
            Info(InfoRecog, 1,
                 "ImageRecogNode of RecogNode <ri> could not be recognised,",
                 " IsReady(<ri>) is not set, recognition aborts");
            return ri;
        fi;

        if IsMatrixGroup(Image(Homom(ri))) then
            Info(InfoRecog,2,"Going to the image (depth=",depth,", try=",
              counter,", dim=",DimensionOfMatrixGroup(Image(Homom(ri))),
              ", field=",Size(FieldOfMatrixGroup(Image(Homom(ri)))),").");
        else
            Info(InfoRecog,2,"Going to the image (depth=",depth,", try=",
              counter,").");
        fi;
        if ForAny(GeneratorsOfGroup(H), x->not ValidateHomomInput(ri, x)) then
            # Our group fails to contain some of the generators of H!
            return fail;
        fi;

        Add(depthString,'F');
        rifac := RecogniseGeneric(
                  Group(List(GeneratorsOfGroup(H), x->ImageElm(Homom(ri),x))),
                  methodsforimage(ri), depthString, InitialDataForImageRecogNode(ri) );
        Remove(depthString);
        PrintTreePos("F",depthString,H);
        SetImageRecogNode(ri,rifac);
        SetParentRecogNode(rifac,ri);

        if IsMatrixGroup(H) then
            Info(InfoRecog,2,"Back from image (depth=",depth,
                 ", dim=",ri!.dimension,", field=",
                 Size(ri!.field),").");
        else
            Info(InfoRecog,2,"Back from image (depth=",depth,").");
        fi;

        if not IsReady(rifac) then
            # IsReady was not set, thus abort the whole computation.
            if InfoLevel(InfoRecog) = 1 and depth = 0 then Print("\n"); fi;
            return ri;
        fi;

        # Now we want to have preimages of the new generators in the image:
        Info(InfoRecog,2,"Calculating preimages of nice generators.");
        ri!.pregensfacwithmem := CalcNiceGens(rifac, ri!.gensHmem);
        Setpregensfac(ri, StripMemory(ri!.pregensfacwithmem));

        # Now create the kernel generators with the stored method:
        succ := CallFuncList(findgensNmeth(ri).method,
                             Concatenation([ri],findgensNmeth(ri).args));
    until succ = true;

    # If nobody has set how we produce preimages of the nicegens:
    if not Hascalcnicegens(ri) then
        Setcalcnicegens(ri,CalcNiceGensHomNode);
    fi;

    # Do a little bit of preparation for the generators of N:
    if not IsBound(ri!.leavegensNuntouched) then
        l := gensN(ri);
        Sort(l,SortFunctionWithMemory);   # this favours "shorter" memories!
        # FIXME: For projective groups different matrices might stand
        #        for the same element, we might overlook this here!
        # remove duplicates:
        ll := [];
        for i in [1..Length(l)] do
            if not isone(ri)(l[i]) and
               (i = 1 or not isequal(ri)(l[i],l[i-1])) then
                Add(ll,l[i]);
            fi;
        od;
        SetgensN(ri,ll);
    fi;
    if IsEmpty(gensN(ri)) and immediateverification(ri) then
        # Special case, for an explanation see the source of the called function.
        RECOG_HandleSpecialCaseKernelTrivialAndMarkedForImmediateVerification(ri);
    fi;
    if IsEmpty(gensN(ri)) then
        # We found out that N is the trivial group!
        # In this case we do nothing, kernel is fail indicating this.
        Info(InfoRecog,2,"Found trivial kernel (depth=",depth,").");
        SetKernelRecogNode(ri,fail);
        # We have to learn from the image, what our nice generators are:
        SetNiceGens(ri,pregensfac(ri));
        if InfoLevel(InfoRecog) = 1 and depth = 0 then Print("\n"); fi;
        # StopStoringRandEls(ri);
        SetFilterObj(ri,IsReady);
        return ri;
    fi;

    Info(InfoRecog,2,"Going to the kernel (depth=",depth,").");
    repeat
        # Now we go on as usual:
        SetgensNslp(ri,SLPOfElms(gensN(ri)));
        SlotUsagePattern(gensNslp(ri));

        # This is now in terms of the generators of H!
        N := Group(StripMemory(gensN(ri)));

        Add(depthString,'K');
        riker := RecogniseGeneric( N, methoddb, depthString, InitialDataForKernelRecogNode(ri) );
        Remove(depthString);
        PrintTreePos("K",depthString,H);
        SetKernelRecogNode(ri,riker);
        SetParentRecogNode(riker,ri);
        Info(InfoRecog,2,"Back from kernel (depth=",depth,").");

        if not IsReady(riker) then
            # IsReady is not set, thus the whole computation aborts.
            return ri;
        fi;
        done := true;
        if immediateverification(ri) then
            Info(InfoRecog,2,"Doing immediate verification (depth=",
                 depth,").");
            done := ImmediateVerification(ri);
        fi;
    until done;

    SetNiceGens(ri,Concatenation(pregensfac(ri), NiceGens(riker)));
    if InfoLevel(InfoRecog) = 1 and depth = 0 then Print("\n"); fi;
    # StopStoringRandEls(ri);
    SetFilterObj(ri,IsReady);
    return ri;
  end);

InstallGlobalFunction( ValidateHomomInput,
  function(ri,x)
    if Hasvalidatehomominput(ri) then
        return validatehomominput(ri)(ri,x);
    else
        return true;
    fi;
  end );

InstallGlobalFunction( CalcNiceGens,
  function(ri,origgens)
    return calcnicegens(ri)(ri,origgens);
  end );

InstallGlobalFunction( CalcNiceGensGeneric,
  # generic function using an slp:
  function(ri,origgens)
    if Hasslptonice(ri) then
        return ResultOfStraightLineProgram(slptonice(ri),origgens);
    else
        return origgens;
    fi;
  end );

InstallGlobalFunction( CalcNiceGensHomNode,
  # function for the situation on a homomorphism node (non-Leaf):
  function(ri, origgens)
    local nicegens, kernelgens;
    # compute preimages of the nicegens of the image group
    nicegens := CalcNiceGens(ImageRecogNode(ri), origgens);
    # Is there a non-trivial kernel? then add its nicegens
    if HasKernelRecogNode(ri) and KernelRecogNode(ri) <> fail then
        # we cannot just use gensN(KernelRecogNode(ri)) here, as those values are defined
        # relative to the original generators we used during recognition; but
        # the origgens passed to this function might differ
        kernelgens := ResultOfStraightLineProgram(gensNslp(ri), origgens);
        Append(nicegens, CalcNiceGens(KernelRecogNode(ri), kernelgens));
    fi;
    return nicegens;
  end );

InstallGlobalFunction( SLPforElement,
  function(ri,x)
    local slp;
    slp := slpforelement(ri)(ri,x);
    if slp <> fail then
        SlotUsagePattern(slp);
    fi;
    return slp;
  end );

InstallGlobalFunction( SLPforElementGeneric,
  # generic method for a non-leaf node
  function(ri,g)
    local gg,n,rifac,riker,s,s1,s2,y,nr1,nr2;
    rifac := ImageRecogNode(ri);
    riker := KernelRecogNode(ri);   # note: might be fail

    if not ValidateHomomInput(ri, g) then
        return fail;
    fi;

    gg := ImageElm(Homom(ri),g);
    if gg = fail then
        return fail;
    fi;
    s1 := SLPforElement(rifac,gg);
    if s1 = fail then
        return fail;
    fi;
    # The corresponding kernel element:
    y := ResultOfStraightLineProgram(s1,pregensfac(ri));
    n := g*y^-1;
    # If the kernel is trivial, we check whether n is the identity in ri.
    # This is necessary during immediate verification, since the kernel might
    # have incorrectly been recognised as trivial.
    if riker = fail then
        if not ri!.isone(n)
                # Big ugly hack. If the successMethod is BlocksModScalars, then
                # we can't use ri!.isone, see the documentation of
                # BlocksModScalars. 
                and not ri!.fhmethsel.successMethod = "BlocksModScalars" then
            return fail;
        fi;
        return s1;
    fi;
    s2 := SLPforElement(riker,n);
    if s2 = fail then
        return fail;
    fi;
    nr2 := NrInputsOfStraightLineProgram(s2);
    nr1 := NrInputsOfStraightLineProgram(s1);
    s := NewProductOfStraightLinePrograms(s2,[nr1+1..nr1+nr2],
                                          s1,[1..nr1],
                                          nr1+nr2);
    return s;
  end);

# Some helper functions for generic code:

InstallOtherMethod( Size, "for a recognition node",
  [IsRecogNode and IsReady],
  function(ri)
    local size;
    if IsLeaf(ri) then
        # Note: A leaf in projective recognition *has* to set the size
        #       of the recognition node!
        return Size(Grp(ri));
    else
        size := Size(ImageRecogNode(ri));
        if KernelRecogNode(ri) <> fail then
            return Size(KernelRecogNode(ri)) * size;
        else
            return size;   # trivial kernel
        fi;
    fi;
  end);

InstallOtherMethod( Size, "for a failed recognition node",
  [IsRecogNode],
  function(ri)
    ErrorNoReturn("the recognition described by this recognition node has failed!");
  end);

InstallOtherMethod( \in, "for a group element and a recognition node",
  [IsObject, IsRecogNode and IsReady],
  function( el, ri )
    local gens,slp;
    slp := SLPforElement(ri,el);
    if slp = fail then
        return false;
    else
        gens := NiceGens(ri);
        if IsObjWithMemory(gens[1]) then
            gens := StripMemory(gens);
        fi;
        return isequal(ri)(el,ResultOfStraightLineProgram(slp,gens));
    fi;
  end);

InstallOtherMethod( \in, "for a group element and a recognition node",
  [IsObject, IsRecogNode],
  function( el, ri )
    ErrorNoReturn("the recognition described by this recognition node has failed!");
  end);

InstallGlobalFunction( "DisplayCompositionFactors", function(arg)
  local c,depth,f,i,j,ri,homs,ksize;
  if Length(arg) = 1 then
      ri := arg[1];
      depth := 0;
      homs := 0;
      ksize := 1;
  else
      ri := arg[1];
      depth := arg[2];
      homs := arg[3];
      ksize := arg[4];
  fi;
  if not IsReady(ri) then
      for j in [1..homs] do Print("-> "); od;
      Print("Recognition failed\n");
      return;
  fi;
  if IsLeaf(ri) then
      c := CompositionSeries(Grp(ri));
      for i in [1..Length(c)-1] do
          if homs > 0 then
              Print("Group with Size ",ksize*Size(c[i]));
              for j in [1..homs] do Print(" ->"); od;
              Print(" ");
          fi;
          Print("Group ",GroupString(c[i],""),"\n | ");
          f := Image( NaturalHomomorphismByNormalSubgroup( c[i], c[i+1] ) );
          Print(IsomorphismTypeInfoFiniteSimpleGroup( f ).name, "\n" );
      od;
  else
      if HasKernelRecogNode(ri) and KernelRecogNode(ri) <> fail then
          DisplayCompositionFactors(ImageRecogNode(ri),depth+1,homs+1,
                                    ksize*Size(KernelRecogNode(ri)));
          DisplayCompositionFactors(KernelRecogNode(ri),depth+1,homs,ksize);
      else
          DisplayCompositionFactors(ImageRecogNode(ri),depth+1,homs+1,ksize);
      fi;
  fi;
  if depth = 0 then
      Print("1\n");
  fi;
end );

InstallGlobalFunction( "SLPforNiceGens", function(ri)
  local l,ll,s;
  l := List( [1..Length(GeneratorsOfGroup(Grp(ri)))], x->() );
  l := GeneratorsWithMemory(l);
  ll := CalcNiceGens(ri,l);
  s := SLPOfElms(ll);
  if s <> fail then
      SlotUsagePattern(s);
  fi;
  return s;
end );

# For user debugging purposes. Takes a string consisting of lower- or
# upper-case "f" and "k". Returns the node one arrives at by descending through
# the tree, going to the factor for each "f" and to the kernel for each "k".
InstallGlobalFunction( "GetCompositionTreeNode",
  function( ri, what )
    local r,c;
    r := ri;
    for c in what do
      if c in "fF" then r := ImageRecogNode(r);
      elif c in "kK" then r := KernelRecogNode(r); fi;
    od;
    return r;
  end );

# Testing:

RECOG.TestGroupOptions := rec(
      # Number of times to test whether the recognized size is right
      sizeTests := 3,

      # Number of random elements in group to check
      # This is used both for the number of elements in the
      # group to check, and the number of random elements of
      # a supergroup to check
      inTests := 30,

      # The following options are off by default as they fail on
      # many examples at present

      # if the following is set to true, then we test what happens if  SLPforElement
      # is called with elements outside the group
      tryNonGroupElements := false
  );


# Test recognition of a group 'g' with known size 'size'.
# 'proj' denotes if the group is projective
# 'optionlist' is an optional list of options overriding
# RECOG.TestGroupOptions
RECOG.TestGroup := function(g,proj,size, optionlist...)
  local l,r,ri,s,x,count,lvl,seedMT,seedRS,gens,supergroup, options;
  count := 0;
  
  options := ShallowCopy(RECOG.TestGroupOptions);

  if Length(optionlist) > 0 then
    for r in RecNames(optionlist[1]) do
        if not IsBound(options.(r)) then
            ErrorNoReturn("Invalid option to TestGroup: ", r);
        fi;
        options.(r) := optionlist[1].(r);
    od;
  fi;

  lvl:=InfoLevel(InfoRecog);
  SetInfoLevel(InfoRecog, 0);
  repeat
      count := count + 1;
      #r := Runtime();
      seedMT := State(GlobalMersenneTwister);
      seedRS := State(GlobalRandomSource);
      if proj then
          ri := RecogniseProjectiveGroup(g);
      else
          ri := RecogniseGroup(g);
      fi;
      #Print("Time for recognition: ",Runtime()-r,"\n");
      if Size(ri) <> size then
          Print("ALARM: set count to -1 to skip test!\n");
          Print("group := ", g, ";\n");
          Print("recogsize := ", Size(ri), ";\n");
          Print("truesize := ", size, ";\n");
          Print("proj := ", proj, ";\n");
          Print("seedMT := ", seedMT, ";\n");
          Print("seedRS := ", seedRS, ";\n");
          Error("Alarm: Size not correct!\n");
          if count = -1 then
              return fail;
          fi;
      else
          #Print("Test was OK!\n");
          count := 3;   # worked!
      fi;
  until count >= 3;
  #View(ri);
  #Print("\n");
  count := 0;
  gens := GeneratorsOfGroup(g);
  # Handle groups where GeneratorsOfGroup returns empty list
  if IsEmpty(gens) then
    gens := [One(g)];
  fi;
  l := CalcNiceGens(ri,gens);
  repeat
      count := count + 1;
      #Print(".\c");
      x := PseudoRandom(g);
      s := SLPforElement(ri,x);
      if s = fail or not isequal(ri)(ResultOfStraightLineProgram(s,l),x) then
          Print("ALARM: set count to -1 to skip test!\n");
          Print("group := ", g, ";\n");
          Print("recogsize := ", Size(ri), ";\n");
          Print("proj := ", proj, ";\n");
          Print("x := ", x, ";\n");
          Print("s := ", s, ";\n");
          if s <> fail then
            Print("result := ", ResultOfStraightLineProgram(s,l), ";\n");
          fi;
          Error("Alarm: SLPforElement did not work!\n");
          
          if count = -1 then
              return fail;
          fi;
      fi;
  until count >= options.inTests;

  if IsPermGroup(g) then
    supergroup := SymmetricGroup(LargestMovedPoint(g) + 2);
  elif IsMatrixGroup(g) then
    supergroup := GL(DimensionOfMatrixGroup(g), DefaultFieldOfMatrixGroup(g));
  else
    supergroup := fail;
  fi;

  if supergroup <> fail and options.tryNonGroupElements then
    count := 0;
    repeat
        count := count + 1;
        #Print(".\c");
        x := PseudoRandom(supergroup);
        s := SLPforElement(ri,x);
        if s <> fail and not isequal(ri)(ResultOfStraightLineProgram(s,l),x) then
            Print("ALARM: set count to -1 to skip test!\n");
            Print("group := ", g, ";\n");
            Print("recogsize := ", Size(ri), ";\n");
            Print("proj := ", proj, ";\n");
            Print("x := ", x, ";\n");
            Print("s := ", s, ";\n");
            if s <> fail then
                Print("result := ", ResultOfStraightLineProgram(s,l), ";\n");
            fi;

            Error("Alarm: SLPforElement did not work on (possibly) non-group element!\n");
            if count = -1 then
                return fail;
            fi;
        fi;
    until count >= options.inTests;
  fi;

  #Print("\n30 random elements successfully sifted!\n");
  SetInfoLevel(InfoRecog, lvl);
  return ri;
end;

# Call RECOG.TestGroup on all maximal subgroups of a named atlas group.
# under all known representations which are permutation groups or
# matrices over finite fields.
# Optionally give 'options' to pass options to RECOG.TestGroup
RECOG.testAllMaximalSubgroupsOfAtlasGroup := function(name, options...)
    local reps, rep, maximal, exhaustList;

    # The following function makes it easy to extract
    # all results of a function which returns 'fail'
    # when given too large a value
    exhaustList := function(F)
        local l, i, val;
        l := [];
        for i in PositiveIntegers do
            val := F(i);
            if val = fail then
                return l;
            fi;
            Add(l, val);
        od;
    end;

    reps := exhaustList(x -> AtlasGenerators(name, x));

    # Only interested in permutations or finite fields
    reps := Filtered(reps, x -> (not IsBound(x.ring)) or (IsFinite(x.ring) and IsField(x.ring)));
    Print("Testing ", Size(reps), " representations\n");
    for rep in reps do
        for maximal in exhaustList(x -> AtlasSubgroup(rep, x)) do
            CallFuncList(RECOG.TestGroup, Concatenation([maximal, false, Size(maximal)], options));
            Print(".");
        od;
        Print("\n");
    od;
end;

# Call RECOG.TestGroup on all subgroups up to conjugacy of a group.
# Optionally give 'options' to pass options to RECOG.TestGroup
RECOG.testAllSubgroups := function(g, options...)
    local list, sub;
    list := List(ConjugacyClassesSubgroups(g), Representative);
    for sub in list do
        CallFuncList(RECOG.TestGroup, Concatenation([sub, false, Size(sub)],options));
    od;
end;


RECOG.TestRecognitionNode := function(ri,stop,recurse)
  local err, grp, x, slp, y, ef, ek, i;
  err := 0;
  grp := Grp(ri);
  for i in [1..100] do
      x := PseudoRandom(grp);
      slp := SLPforElement(ri,x);
      if slp <> fail then
          y := ResultOfStraightLineProgram(slp,NiceGens(ri));
      fi;
      if slp = fail or not ri!.isone(x/y) then
          if stop then ErrorNoReturn("ErrorNoReturn found, look at x, slp and y"); fi;
          err := err + 1;
          Print("X\c");
      else
          Print(".\c");
      fi;
  od;
  Print("\n");
  if err > 0 and recurse then
      if IsLeaf(ri) then
          return rec(err := err, badnode := ri);
      fi;
      ef := RECOG.TestRecognitionNode(ImageRecogNode(ri),stop,recurse);
      if IsRecord(ef) then
          return ef;
      fi;
      if KernelRecogNode(ri) <> fail then
          ek := RECOG.TestRecognitionNode(KernelRecogNode(ri),stop,recurse);
          if IsRecord(ek) then
              return ek;
          fi;
      fi;
      return rec( err := err, badnode := ri, factorkernelok := true );
  fi;
  return err;
end;

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