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


Quelle  genss.gi   Sprache: unbekannt

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

#############################################################################
##
##  genss.gi              genss package           
##                                                           Max Neunhoeffer
##                                                              Felix Noeske
##
##  Copyright 2006 Lehrstuhl D für Mathematik, RWTH Aachen
##
##  Implementation stuff for generic Schreier-Sims
##
#############################################################################

#############################################################################
# The following global record contains default values for options for the 
# main function "StabilizerChain":
#############################################################################

# Initial hash size for orbits:
GENSS.InitialHashSize := NextPrimeInt(1000);
# Number of points to process before reporting:
GENSS.Report := 30000;
# Number of random elements to consider for the determination of short orbits:
GENSS.ShortOrbitsNrRandoms := 10;
# Absolute limit for orbit length in search for short orbits:
GENSS.ShortOrbitsOrbLimit := 80000;
# First limit for the parallel enumeration of orbits looking for short orbits:
GENSS.ShortOrbitsInitialLimit := 400;
# Absolute limit for single orbit length:
GENSS.OrbitLengthLimit := 10000000;
# Number of points in the previous orbit to consider for the next base point:
GENSS.NumberPrevOrbitPoints := 10;
# Number of (evenly distributed) random generators for the stabilizer:
GENSS.RandomStabGens := 3;
# Product replacement parameters for the stabilizer element generation:
# Now actually used for the generation of all random elements on top
# level. Random elements further down are created on top and then
# sifted.
GENSS.StabGenScramble := 30;
GENSS.StabGenScrambleFactor := 6;
GENSS.StabGenAddSlots := 3;
GENSS.StabGenMaxDepth := 400;
# Number of random elements used for verification,
# note that this is changed by the "random" and "ErrorBound" options!
GENSS.VerifyElements := 10;   # this amounts to an error bound of 1/1024
GENSS.DeterministicVerification := false;
# Number of random elements used to do immediate verification:
GENSS.ImmediateVerificationElements := 3;
# Are we working projectively?
GENSS.Projective := false;
# To find a very short orbit we try two basis vectors and a random vector:
GENSS.VeryShortOrbLimit := 500;
# Never consider more than this number of candidates for short orbits:
GENSS.LimitShortOrbCandidates := 50;
# Do not throw Errors but return fail:
GENSS.FailInsteadOfError := false;
# Number of Schreier generators to create in TC verification:
GENSS.NumberSchreierGens := 20;
# Maximal number of Schreier generators to create in TC verification:
GENSS.MaxNumberSchreierGens := 100;
# By default do 3 rounds of birthday paradox method:
GENSS.TryBirthdayParadox := 3;
# By default do 1 short orbit tries:
GENSS.TryShortOrbit := 1;
# Limit for orbit length during orbit estimation using birthday paradox:
GENSS.OrbitLimitBirthdayParadox := 1000000;
# We immediately take an orbit if its estimate is lower than this limit:
GENSS.OrbitLimitImmediatelyTake := 10000;
# Limit for number of random elements during orbit estimation:
GENSS.NrRandElsBirthdayParadox := 6000;
# Set this to false to allow orbits of length 1:
GENSS.Reduced := true;

# Product replacement parameters for Stab:
GENSS.StabScramble := 10;
GENSS.StabScrambleFactor := 1;
GENSS.StabAddSlots := 1;
GENSS.StabMaxDepth := 400;
# Initial limit for orbit length for Stab computation:
GENSS.StabInitialLimit := 1000;
# Patience to find random elements mapping the orbit into itself:
GENSS.StabInitialPatience := 10;
# Maximal amount of memory used for the orbit:
GENSS.StabOrbitLimit := 1000000;
# If the probability of a wrong stabiliser is smaller than this, do no
# longer try to create stabiliser elements:
GENSS.StabAssumeCompleteLimit := 1/(10^7);
# The following switches off the log used in all orbits for stabilizer
# chains if set to false. Do not do this unless you know what you are
# doing since since could prevent Schreier trees from being shallow.
GENSS.OrbitsWithLog := true;

#############################################################################
# A few helper functions needed elsewhere:
#############################################################################

InstallGlobalFunction( GENSS_CopyDefaultOptions,
  function( defopt, opt )
    local n;
    for n in RecNames(defopt) do
        if not(IsBound(opt.(n))) then
            opt.(n) := defopt.(n);
        fi;
    od;
  end );

InstallGlobalFunction( GENSS_MapBaseImage,
  function( bi, el, S )
    # bi must be a base image belonging to S, el a group element
    local i,l,res;
    l := Length(bi);
    res := 0*[1..l];
    i := 1;
    while true do
        res[i] := S!.orb!.op(bi[i],el);
        i := i + 1;
        if i = l+1 then break; fi;
        S := S!.stab;
    od;
    return res;
  end );

InstallGlobalFunction( GENSS_FindVectorsWithShortOrbit,
  # This implements Murray/O'Brien-like heuristics.
  # It produces new random elements using GENSS_RandomElementFromAbove
  # and stores them in opt.FindBasePointCandidatesData.randpool for
  # later usage.
  function(g,opt,parentS)
    # Needs opt components "ShortOrbitsNrRandoms"
    local l, f, data, x, c, onlydegs, v, vv, w, i, nw, inters, sortfun, 
          wb, j, ww;
    Info(InfoGenSS,4,"Trying Murray/O'Brien heuristics...");
    l := ShallowCopy(GeneratorsOfGroup(g));
    f := DefaultFieldOfMatrixGroup(g);
    data := opt.FindBasePointCandidatesData;
    for i in [1..opt.ShortOrbitsNrRandoms] do
        if parentS = false then
            x := GENSS_RandomElementFromAbove(opt,0);
        else
            x := GENSS_RandomElementFromAbove(parentS,parentS!.layer);
        fi;
        Add(l,x);
        Add(data.randpool,x);
    od;
    ForgetMemory(l);
    c := List(l,x->Set(Factors(CharacteristicPolynomial(x,1):
                               onlydegs := [1..3])));
    v := [];
    for i in [1..Length(l)] do
        for j in [1..Length(c[i])] do
            vv := EmptyPlist(Length(c[i]));
            Add(vv,[NullspaceMat(Value(c[i][j],l[i])),
                    Degree(c[i][j]),
                    WeightVecFFE(CoefficientsOfLaurentPolynomial(c[i][j])[1]),
                    1]);
        od;
        Add(v,vv);
    od;
    Info(InfoGenSS,4,"Have eigenspaces.");
    # Now collect a list of all those spaces together with all
    # possible intersects:
    w := [];
    i := 1;
    while i <= Length(l) and Length(w) < GENSS.LimitShortOrbCandidates do
        nw := [];
        for j in [1..Length(v[i])] do
            for ww in w do
                inters := SumIntersectionMat(ww[1],v[i][j][1])[2];
                if Length(inters) > 0 then
                    Add(nw,[inters,Minimum(ww[2],v[i][j][2]),
                            Minimum(ww[3],v[i][j][3]),ww[4]+v[i][j][4]]);
                fi;
            od;
            Add(nw,v[i][j]);
        od;
        Append(w,nw);
        i := i + 1;
    od;
    sortfun := function(a,b)
        if a[2] < b[2] then return true;
        elif a[2] > b[2] then return false;
        elif a[3] < b[3] then return true;
        elif a[3] > b[3] then return false;
        elif a[4] < b[4] then return true;
        elif a[4] > b[4] then return false;
        elif Length(a[1]) < Length(b[1]) then return true;
        else return false;
        fi;
    end;
    Sort(w,sortfun);
    wb := List(w,ww->ww[1][1]);
    Info(InfoGenSS,3,"Have ",Length(wb)," vectors for possibly short orbits.");
    for ww in [1..Length(wb)] do
        if not(IsMutable(wb[ww])) then
            wb[ww] := ShallowCopy(wb[ww]);
        fi;
        ORB_NormalizeVector(wb[ww]);
    od;
    return wb;
end );

InstallGlobalFunction( GENSS_FindShortOrbit,
  function( g, opt, parentS )
    # Needs opt components:
    #  "ShortOrbitsNrRandoms"  (because it uses GENSS_FindVectorsWithShortOrbit)
    #  "ShortOrbitsOrbLimit"
    #  "ShortOrbitsInitialartLimit"
    local ThrowAwayOrbit,found,gens,hashlen,i,j,limit,newnrorbs,nrorbs,o,wb;

    wb := GENSS_FindVectorsWithShortOrbit(g,opt,parentS);
    if Length(wb) = 0 then return fail; fi;

    # Now we have a list of vectors with (hopefully) short orbits.
    # We start enumerating all those orbits, but first only 50 elements:
    nrorbs := Minimum(Length(wb),64);  # take only the 64 first
    gens := GeneratorsOfGroup(g);
    o := [];
    hashlen := NextPrimeInt(QuoInt(opt.ShortOrbitsOrbLimit,2));
    for i in [1..nrorbs] do
        Add(o,Orb(gens,ShallowCopy(wb[i]),OnLines,
                  rec( treehashsize := hashlen )));
    od;
    limit := opt.ShortOrbitsInitialLimit;
    i := 1;               # we start to work on the first one

    ThrowAwayOrbit := function(i)
        # This removes orbit number i from o, thereby handling nrorbs and
        # Length(o) correctly. If you want to use o[i] further, please
        # make a copy (of the reference) before calling this function.
        if Length(o) > nrorbs then
            o[i] := o[nrorbs+1];
            o{[nrorbs+1..Length(o)-1]} := o{[nrorbs+2..Length(o)]};
            Unbind(o[Length(o)]);
        else
            o{[i..nrorbs-1]} := o{[i+1..nrorbs]};
            Unbind(o[nrorbs]);
            nrorbs := nrorbs-1;
        fi;
    end;

    repeat
        Enumerate(o[i],limit);
        found := IsClosed(o[i]);
        if Length(o[i]) = 1 then
            Info(InfoGenSS,3,"Orbit Number ",i," has length 1.");
            found := false;
            # Now throw away this orbit:
            ThrowAwayOrbit(i);
            # we intentionally do not increase i here!
        elif not(found) then
            i := i + 1;
        fi;
        if i > nrorbs then
          Info(InfoGenSS,3,"Done ",nrorbs,
               " orbit(s) to limit ",limit,".");
          limit := limit * 2;
          if limit > opt.ShortOrbitsOrbLimit then
              Info(InfoGenSS,3,"Limit reached, giving up.");
              return fail;
          fi;
          i := 1;
          if nrorbs < i then
              Info(InfoGenSS,3,"No orbits left, giving up.");
              return fail;
          fi;
          if nrorbs > 1 then
              newnrorbs := QuoInt((nrorbs+1),2);
              for j in [newnrorbs+1..nrorbs] do
                  Unbind(o[j]);
              od;
              nrorbs := newnrorbs;
          fi;
        fi;
    until found;
    Info(InfoGenSS,2,"Found short orbit of length ",Length(o[i])," (#",i,").");
    return o[i];
  end );   

InstallGlobalFunction( GENSS_IsOneProjective,
  function(el)
    local s;
    s := el[1][1];
    if IsZero(s) then return false; fi;
    if not(IsOne(s)) then
        s := s^-1;
        el := s * el;
    fi;
    return IsOne( el );
  end );


#############################################################################
# Now to the heart of the method, the Schreier-Sims:
#############################################################################

InstallMethod( FindBasePointCandidates, 
  "for a scalar matrix group over a FF",
  [ IsGroup and IsMatrixGroup and IsFinite, IsRecord, IsInt, IsObject ],
  50,  # highest weight,
  function( grp, opt, i, parentS )
    local F, q, d, gens, v;
    Info( InfoGenSS, 3, "Finding nice base points (scalar)..." );
    F := DefaultFieldOfMatrixGroup(grp);
    q := Size(F);
    d := DimensionOfMatrixGroup(grp);
    gens := GeneratorsOfGroup(grp);
    if IsObjWithMemory(gens[1]) then
        gens := StripMemory(gens);
    fi;
    if ForAny(gens,x->not(GENSS_IsOneProjective(x))) then
        opt.FindBasePointCandidatesData := rec( randpool := [], vecs := [] );
        # This is needed for communication between different methods
        TryNextMethod();
    fi;
    v := ZeroMutable(gens[1][1]);
    v[1] := PrimitiveRoot(F);   # to indicate the field!
    return rec( points := [v], ops := [OnRight], used := 0 );
  end );

InstallMethod( FindBasePointCandidates, 
  "for a matrix group over a FF, very short orbit",
  [ IsGroup and IsMatrixGroup and IsFinite, IsRecord, IsInt, IsObject ],
  40,  # highest weight,
  function( grp, opt, i, parentS )
    local F, q, d, gens, op, v, vv, k, kk, o, cand, j;
    Info( InfoGenSS, 3, "Finding nice base points (very short)..." );
    F := DefaultFieldOfMatrixGroup(grp);
    q := Size(F);
    d := DimensionOfMatrixGroup(grp);
    gens := GeneratorsOfGroup(grp);
    if IsObjWithMemory(gens[1]) then
        gens := StripMemory(gens);
    fi;

    # Try two standard basis vectors and a random vector to find a very 
    # short orbit:
    if q = 2 then
        op := OnPoints;
    else
        op := OnLines;
    fi;
    v := [];
    # Find first standard basis vector that is moved:
    vv := ZeroMutable( gens[1][1] );
    k := 1;
    while k <= d do
        vv[k] := One(F);
        if ForAny(gens,x->op(vv,x) <> vv) then
            Add(v,vv);
            break;
        fi;
        vv[k] := Zero(F);
        k := k + 1;
    od;
    # Find last standard basis vector that is moved:
    vv := ZeroMutable( gens[1][1] );
    kk := d;
    while kk >= k do
        vv[kk] := One(F);
        if ForAny(gens,x->op(vv,x) <> vv) then
            Add(v,vv);
            break;
        fi;
        vv[kk] := Zero(F);
        kk := kk - 1;
    od;
    # Pick a random vector:
    vv := ZeroMutable( gens[1][1] );
    if IsPlistRep(vv) then
        for j in [1..Length(vv)] do
            vv[j] := Random(F);
        od;
    else
        Randomize(vv);
    fi;
    ORB_NormalizeVector(vv);
    Add(v,vv);
    # Now investigate these up to a certain limit:
    for j in [1..Length(v)] do
        o := Orb(gens,v[j],op, 
                 rec(treehashsize := QuoInt(opt.VeryShortOrbLimit,2)+1));
        Enumerate(o,opt.VeryShortOrbLimit);
        if Length(o) > 1 and Length(o) < opt.VeryShortOrbLimit then
            Info( InfoGenSS, 3, "Found orbit of length ",Length(o) );
            cand := rec( points := [v[j]], ops := [op], used := 0 );
            # Note that if we work non-projectively, then the same
            # point will be taken next with OnRight automatically!
            return cand;
        fi;
    od;
    Info( InfoGenSS, 3, "Found no very short orbit up to limit ",
          opt.VeryShortOrbLimit );
    Append(opt.FindBasePointCandidatesData.vecs,v);  # hand on vectors
    TryNextMethod();
  end );

# Method with rank 30 taking some commutators and invariant spaces

InstallMethod( FindBasePointCandidates,
  "for a matrix group over a FF, using birthday paradox method",
  [ IsGroup and IsMatrixGroup and IsFinite, IsRecord, IsInt, IsObject ], 20,
  function( grp, opt, mode, parentS )
    local F, q, d, randels, immorblimit, orblimit, data, op, v, l, c, e, ht, 
          val, x, w, cand, minest, minpos, round, i, j, gens;
    F := DefaultFieldOfMatrixGroup(grp);
    q := Size(F);
    d := DimensionOfMatrixGroup(grp);
    randels := opt.NrRandElsBirthdayParadox;
    immorblimit := opt.OrbitLimitImmediatelyTake;
    orblimit := opt.OrbitLimitBirthdayParadox;

    Info( InfoGenSS, 3, "Finding base points (birthday paradox, limit=",
                        orblimit,", randels=",randels,")..." );
    data := opt.FindBasePointCandidatesData; # this we get from earlier methods
    if q = 2 then
        op := OnPoints;
    else
        op := OnLines;
    fi;
    gens := GeneratorsOfGroup(grp);
    if IsObjWithMemory(gens[1]) then
        gens := StripMemory(gens);
    fi;
    for round in [1..opt.TryBirthdayParadox] do
        v := Set(GENSS_FindVectorsWithShortOrbit(grp,opt,parentS));
        if round = 1 then
            Append(v,data.vecs);   # take previously tried ones as well
        fi;
        v := Filtered(v,vv->ForAny(gens,x-> vv <> op(vv,x)));
        l := Length(v);
        if l = 0 then
            # Find a vector on which grp acts non-trivially.
            # At least one basis vector is guaranteed to be moved,
            # unless grp is diagonal and op is OnLines, in which case
            # they might all be fixed. However, in that case, the
            # vector [1,1,...,1] is moved.
            v := OneMutable(gens[1]); # List of basis vectors
            Add(v, Sum(v)); # add vector [1,1,...,1]
            v := Filtered(v,vv->ForAny(gens,x-> vv <> op(vv,x)));
            l := Length(v);
        fi;
        c := 0*[1..l];    # the number of coincidences
        e := ListWithIdenticalEntries(l,infinity);   # the current estimates
        ht := HTCreate(v[1]*PrimitiveRoot(F),
                       rec(hashlen := NextPrimeInt(l * randels * 4)));
        for i in [1..l] do
            val := HTValue(ht,v[i]);
            if val = fail then
                HTAdd(ht,v[i],[i]);
            else
                AddSet(val,i);
            fi;
        od;
        for i in [1..randels] do
            if parentS = false then
                x := GENSS_RandomElementFromAbove(opt,0);
            else
                x := GENSS_RandomElementFromAbove(parentS,parentS!.layer);
            fi;
            Add(data.randpool,x);
            for j in [1..l] do
                if IsObjWithMemory(x) then
                    w := op(v[j],x!.el);
                else
                    w := op(v[j],x);
                fi;
                val := HTValue(ht,w);
                if val <> fail then   # we know this point!
                    if j in val then    # a coincidence!
                        c[j] := c[j] + 1;
                        e[j] := QuoInt(i^2,2*c[j]);
                        if (c[j] >= 3 and e[j] <= immorblimit) or
                           (c[j] >= 15 and e[j] <= orblimit) then
                             Info( InfoGenSS, 2, "Found orbit with estimated ",
                                   "length ",e[j]," (coinc=",c[j],")" );
                             cand := rec(points := [v[j]], ops := [op], 
                                         used := 0);
                             for i in [1..l] do
                                 if i <> j and c[i] >= 10 and
                                    e[i] <= orblimit then
                                     Add(cand.points,v[i]);
                                     Add(cand.ops,op);
                                 fi;
                             od;
                             if Length(cand.points) > 1 then
                                 Info( InfoGenSS, 2, "Adding ", 
                                       Length(cand.points)-1, " more vectors",
                                       " to candidates.");
                             fi;
                             return cand;
                        fi;
                    else
                        AddSet(val,j);
                    fi;
                else
                    HTAdd(ht,w,[j]);
                fi;
            od;
        od;
        minest := Minimum(e);
        minpos := Position(e,minest);
        Info( InfoGenSS,2,"Birthday #", round, ": no small enough estimate. ",
              "MinEst=",minest," Coinc=",c[j] );
        randels := randels * 2;
        orblimit := orblimit * 4;
    od;
    TryNextMethod();
  end );


InstallMethod( FindBasePointCandidates, 
  "for a matrix group over a FF, original try short orbit method",
  [ IsGroup and IsMatrixGroup and IsFinite, IsRecord, IsInt, IsObject ], 10,
  function( grp, opt, i, parentS )
    local F, d, data, cand, res;
    F := DefaultFieldOfMatrixGroup(grp);
    d := DimensionOfMatrixGroup(grp);
    data := opt.FindBasePointCandidatesData;
    Info( InfoGenSS, 3, "Finding nice base points (TryShortOrbit)..." );

    # Next possibly "TryShortOrbit":
    cand := rec( points := [], used := 0 );
    if IsBound(opt.TryShortOrbit) and opt.TryShortOrbit > 0 then
        repeat
            opt.TryShortOrbit := opt.TryShortOrbit - 1;
            Info(InfoGenSS,1,"Looking for short orbit (",opt.TryShortOrbit,
                 ")...");
            res := GENSS_FindShortOrbit(grp,opt,parentS);
        until res <> fail or opt.TryShortOrbit = 0;
        if res <> fail then
            if Size(F) > 2 then
                cand.points := [res[1]];
                cand.ops := [OnLines];
                # Note that if we work non-projectively, then the same
                # point will be taken next with OnRight automatically!
            else
                cand.points := [res[1]];
                cand.ops := [OnRight];
            fi;
            return cand;
        fi;
    fi;
    TryNextMethod();
  end );

InstallMethod( FindBasePointCandidates, 
  "for a matrix group over a FF, traditional Murray/O'Brien", 
  [ IsGroup and IsMatrixGroup and IsFinite, IsRecord, IsInt, IsObject ],
  function( grp, opt, i, parentS )
    local F, d, bv, cand, w, v;
    F := DefaultFieldOfMatrixGroup(grp);
    d := DimensionOfMatrixGroup(grp);
    Info( InfoGenSS, 3, "Finding nice base points (Murray/O'Brien)..." );

    # Standard Murray/O'Brien heuristics:
    if i = 0 and 
       ((opt!.Projective = false and Size(F)^d > 300000) or
        (opt!.Projective = true and Size(F)^(d-1) > 300000)) then
        bv := GENSS_FindVectorsWithShortOrbit(grp,opt,parentS);
        if Length(bv) < 3 then
            bv := MutableCopyMat(One(grp));
        fi;
        bv := bv{[1..3]};   # just take 3 of them
    else
        bv := One(grp);
    fi;
    cand := rec( points := [], ops := [], used := 0 );
    for v in bv do
        w := ORB_NormalizeVector(ShallowCopy(v));
        if Size(F) = 2 then
            Add(cand.points,w);
            Add(cand.ops,OnRight);
        else
            Add(cand.points,w);
            Add(cand.ops,OnLines);
            # Note that if we work non-projectively, then the same
            # point will be taken next with OnRight automatically!
        fi;
    od;
    return cand;
  end );

InstallMethod( FindBasePointCandidates, "for a permutation group",
  [ IsGroup and IsPermGroup, IsRecord, IsInt, IsObject ],
  function( grp, opt, i, parentS )
    local ops,points;
    if i = 0 then
        points := [1..Minimum(20,LargestMovedPoint(grp))];
    else
        points := [1..LargestMovedPoint(grp)];
    fi;
    ops := List([1..Length(points)],x->OnPoints);
    return rec( points := points, ops := ops, used := 0 );
  end );
    
GENSS_HACK := OnPoints;

InstallGlobalFunction( GENSS_OpFunctionMaker, function(op,index)
  local name,s,f;
  name := NAME_FUNC(op);
  if name = "unknown" or name = "GENSS_HACK" then return fail; fi;
  s := Concatenation( "GENSS_HACK := function(x,el) return ",
                      name, "(x,el[",String(index),"]); end;" );
  f := InputTextString(s);
  Read(f);
  return GENSS_HACK;
end);

InstallMethod( FindBasePointCandidates, "for a direct product",
  [ IsGroup, IsRecord, IsInt, IsObject ],
  function( grp, opt, i, parentS )
    local gens,l,cand,j,factgens,fac,cand2,k,op,S2,op2;
    gens := GeneratorsOfGroup(grp);
    if not(ForAll(gens,IsDirectProductElement)) or Length(gens) = 0 then
        TryNextMethod();
    fi;
    l := Length(gens[1]);
    cand := rec( points := [], ops := [] );
    for j in [1..l] do
        factgens := List(gens,x->x[j]);
        fac := Group(factgens);
        S2 := StabilizerChain(fac);
        cand2 := BaseStabilizerChain(S2);
        for k in [1..Length(cand2.ops)] do
            op := cand2.ops[k];
            op2 := GENSS_OpFunctionMaker(op,j);
            if op2 <> fail then
                Add(cand.points,cand2.points[k]);
                Add(cand.ops,op2);
            fi;
        od;
    od;
    cand.used := 0;
    return cand;
  end );


InstallGlobalFunction( GENSS_NextBasePoint, 
  function( gens, cand, opt, S )
    local NotFixedUnderAllGens,i,notfixed;

    if IsBound(opt.FindBasePointCandidatesData) then
        Unbind(opt.FindBasePointCandidatesData);
    fi;

    NotFixedUnderAllGens := function( gens, x, op )
      if IsObjWithMemory(gens[1]) then
        return ForAny( gens, g->op(x,g!.el) <> x );
      else
        return ForAny( gens, g->op(x,g) <> x );
      fi;
    end;

    # S can be false or a stabilizer chain record
    if S <> false and not(opt.StrictlyUseCandidates) then  
        # try points in previous orbit
        for i in [2..Minimum(Length(S!.orb),opt.NumberPrevOrbitPoints)] do
            if NotFixedUnderAllGens(gens,S!.orb[i],S!.orb!.op) then
                Info(InfoGenSS,3,"Taking another point in same orbit.");
                return rec( point := S!.orb[i], op := S!.orb!.op, 
                            cand := cand );
            fi;
        od;
        # Maybe we can take the last base point now acting non-projectively?
        if opt.Projective = false and IsIdenticalObj(S!.orb!.op,OnLines) and 
           NotFixedUnderAllGens(gens,S!.orb[1],OnRight) then
            Info(InfoGenSS,3,"Taking same point in non-projective action.");
            return rec( point := S!.orb[1], op := OnRight, cand := cand );
        fi;
    fi;

    # Now use the candidates we already have:
    repeat
        if cand.used >= Length(cand.points) then
            if IsBound(cand.points2) and Length(cand.points2) > 0 then
                cand := rec( points := cand.points2, ops := cand.ops2,
                             used := 0 );
            else
                cand := FindBasePointCandidates(Group(gens),opt,1,S);
                opt.StrictlyUseCandidates := false;
                opt.Reduced := true;
            fi;
        fi;
        cand.used := cand.used + 1;
        notfixed := NotFixedUnderAllGens(gens,
                          cand.points[cand.used],cand.ops[cand.used]);
        if notfixed = false and opt.Reduced = true then # everything is fixed!
            if IsBound(cand.points2) then
                # Maybe our current stabilizer is too small, we still
                # might want to use these points again:
                Add(cand.points2,cand.points[cand.used]);
                Add(cand.ops2,cand.ops[cand.used]);
            fi;
        fi;
    until opt.Reduced = false or notfixed;
          
    return rec( point := cand.points[cand.used], op := cand.ops[cand.used],
                cand := cand );
  end );


InstallGlobalFunction( GENSS_CreateStabChainRecord,
  function( parentS, gens, size, nextpoint, nextop, cand, opt )
    # parentS can be false or the parent in the stabiliser chain
    local base, layer, stronggens, layergens, nr, orb, S, hashsize;

    Info( InfoGenSS, 4, "Creating new stab chain record..." );

    if parentS = false then
        base := [];
        layer := 1;
        stronggens := ShallowCopy(gens);
        layergens := [1..Length(gens)];   # indices in stronggens
    else
        base := parentS!.base;
        layer := parentS!.layer + 1;
        stronggens := parentS!.stronggens;
        nr := Length(stronggens);
        Append(stronggens,gens);
        layergens := [nr+1..Length(stronggens)];
    fi;

    gens := ShallowCopy(gens);
    # Note that we do ShallowCopy such that the original list and the
    # one in the orbit record are different from each other.
    if IsInt(size) then
        hashsize := NextPrimeInt(Minimum(size,opt.InitialHashSize));
    else
        hashsize := opt.InitialHashSize;
    fi;
    orb := Orb( gens, nextpoint, nextop,
                rec( treehashsize := hashsize, schreier := true, 
                     log := opt.OrbitsWithLog,
                     report := opt.Report ) );
    S := rec( stab := false, orb := orb, cand := cand, base := base,
              opt := opt, layer := layer, parentS := parentS,
              stronggens := stronggens, layergens := layergens,
              size := size, randpool := [], IsOne := opt.IsOne );
    if parentS = false then
        S!.topS := S;
    else
        S!.topS := parentS!.topS;
    fi;
    if IsBound(opt.FindBasePointCandidatesData) then
        S.randpool := opt.FindBasePointCandidatesData.randpool;
    fi;

    Add(base,nextpoint);
    S.orb!.stabilizerchain := S;
    Objectify( StabChainByOrbType, S );

    return S;
  end );

InstallGlobalFunction( GENSS_RandomElementFromAbove,
  function( S, i )
    # This function provides a random element for the i-th stabiliser
    # in the chain "from above". These elements eventually come from
    # the one product replacer object in the opt record for the whole
    # group. They are sifted through i layers of the stabiliser chain
    # infrastructure until they stabilise the first i points (i=0 simply
    # means a random element in the whole group). If we find out underways
    # that an orbit is too small (that is, a stabiliser was not complete),
    # we fix the stabiliser chain as we go. Random elements that have
    # been generated in a layer j < i previously and are not yet used
    # as strong generators can be taken from a pool that was kept in
    # the stabiliser chain. S must either be layer i of the stabiliser 
    # or (for i=0) it can be equal to the options record opt chain.
    local SS, x, topS, j, o, p, po;
    if i = 0 then
        return Next(S.pr);    # in this case we got the options record
    fi;
    if S!.layer <> i then
        Error("i must be equal to the layer");
    fi;
    SS := S;
    while true do   # will be left by break
        if Length(SS!.randpool) > 0 then
            x := Remove(SS!.randpool,Length(SS!.randpool));
            topS := SS!.topS;
            break;
        fi;
        if SS!.parentS = false then   # we have reached the top
            if IsBound(SS!.opt.RandomElmFunc) then
                x := SS!.opt.RandomElmFunc();
            else
                x := Next(SS!.opt.pr);
            fi;
            topS := SS;   # remember top
            break;
        fi;
        SS := SS!.parentS;
    od;
    # We now have a random element x in layer SS!.layer, that is,
    # it is contained in the SS!.layer-1-th stabiliser, sift it down:
    j := SS!.layer-1;
    while j < i do
        o := SS!.orb;
        if IsObjWithMemory(x) then
          p := o!.op(o[1],x!.el);
        else
          p := o!.op(o[1],x);
        fi;
        po := Position(o,p);
        if po = fail then   # not in current stabilizer
            Info(InfoGenSS,3,"Random element from top found error in layer ",
                             SS!.layer);
            AddGeneratorToStabilizerChain(topS,x);
            # We add it at the top to add it in every orbit above except
            # the first one!
            return GENSS_RandomElementFromAbove(S,i);
        fi;
        # Now sift through Schreier tree:
        while po > 1 do
            x := x * SS!.orb!.gensi[o!.schreiergen[po]];
            po := o!.schreierpos[po];
        od;
        SS := SS!.stab;
        j := j + 1;
    od;
    # After this, we have successfully reached i-th stabilizer
    return x;
  end );
   
InstallGlobalFunction( GENSS_StabilizerChainInner,
  function( gens, size, layer, cand, opt, parentS )
    # Computes a stabilizer chain for the group generated by gens
    # with known size size (can be false if not known). This will be
    # layer layer in the final stabilizer chain. cand is a (shared)
    # record for base point candidates and opt the (shared) option
    # record. This is called in StabilizerChain and calls itself.
    # It also can be called if a new layer is needed.
    local base,gen,S,i,merk,merk2,next,pr,r,stabgens,x;

    Info(InfoGenSS,4,"Entering GENSS_StabilizerChainInner layer=",layer);
    next := GENSS_NextBasePoint(gens,cand,opt,parentS);
    cand := next.cand;   # This could have changed
    S := GENSS_CreateStabChainRecord(parentS,gens,size,
                                     next.point,next.op,next.cand,opt);
    base := S!.base;

    Info( InfoGenSS, 3, "Entering orbit enumeration layer ",layer,"..." );
    repeat
        Enumerate(S!.orb,opt.OrbitLengthLimit);
        if not(IsClosed(S!.orb)) then
            if opt.FailInsteadOfError then
                return "Orbit too long, increase opt.OrbitLengthLimit";
            else
                Error("Orbit too long, increase opt.OrbitLengthLimit");
            fi;
        fi;
    until IsClosed(S!.orb);
    Info(InfoGenSS, 2, "Layer ", layer, ": Orbit length is ", Length(S!.orb)); 

    if layer > 1 then
        parentS!.stab := S;   # such that from now on random element
                              # generation works!
    else
        if (Length(S!.orb) > 50 or S!.orb!.depth > 5) and
           S!.opt.OrbitsWithLog then
            Info(InfoGenSS, 3, "Trying to make Schreier tree shallower (depth=",
                 S!.orb!.depth,")...");
            merk := Length(S!.orb!.gens);
            merk2 := Length(S!.stronggens);
            MakeSchreierTreeShallow(S!.orb);
            Append(S!.stronggens,S!.orb!.gens{[merk+1..Length(S!.orb!.gens)]});
            Append(S!.layergens,[merk2+1..Length(S!.stronggens)]);
            Info(InfoGenSS, 3, "Depth is now ",S!.orb!.depth);
        fi;
    fi;
    S!.orb!.gensi := List(S!.orb!.gens,x->x^-1);
 

    # Are we done?
    if size <> false and Length(S!.orb) = size then
        S!.proof := true;
        Info(InfoGenSS,4,"Leaving GENSS_StabilizerChainInner layer=",layer);
        return S;
    fi;

    # Now create a few random stabilizer elements:
    stabgens := EmptyPlist(opt.RandomStabGens);
    for i in [1..opt.RandomStabGens] do
        x := GENSS_RandomElementFromAbove(S,layer);
        if not(S!.IsOne(x)) then
            Add(stabgens,x);
        fi;
    od;
    Info(InfoGenSS,3,"Created ",opt.RandomStabGens,
         " random stab els, ",
         Length(stabgens)," non-trivial.");
    if Length(stabgens) > 0 then   # there is a non-trivial stabiliser
        Info(InfoGenSS,3,"Found ",Length(stabgens)," non-trivial ones.");
        if size <> false then
            S!.stab := GENSS_StabilizerChainInner(stabgens,size/Length(S!.orb),
                                                   layer+1,cand,opt,S);
        else
            S!.stab := GENSS_StabilizerChainInner(stabgens,false,
                                                   layer+1,cand,opt,S);
        fi;
        if IsString(S!.stab) then return S!.stab; fi; 
        if opt.ImmediateVerificationElements > 0 then
            Info(InfoGenSS,2,"Doing immediate verification in layer ",
                 S!.layer," (",opt.ImmediateVerificationElements,
                 " elements)...");
            i := 0;
#Error("foobar");
            while i < opt.ImmediateVerificationElements do
                i := i + 1;
                x := GENSS_RandomElementFromAbove(S,layer);
                if AddGeneratorToStabilizerChain(S!.topS,x) then
                    Info( InfoGenSS, 2, "Immediate verification found error ",
                          "(layer ",S!.layer,")..." );
                    i := 0;
                fi;
            od;
        fi;

        S!.proof := S!.stab!.proof;   # hand up information
    else
        # We are not sure that the next stabiliser is trivial, but we believe!
        Info(InfoGenSS,3,"Found no non-trivial ones.");
        S!.proof := false;
    fi;

    Info(InfoGenSS,4,"Leaving GENSS_StabilizerChainInner layer=",layer);
    return S;
  end );

InstallGlobalFunction( GENSS_DeriveCandidatesFromStabChain,
  function( S )
    local cand;
    cand := rec( points := [], ops := [], used := 0 );
    while S <> false do
        Add( cand.points, S!.orb[1] );
        Add( cand.ops,    S!.orb!.op );
        S := S!.stab;
    od;
    return cand;
  end );

InstallGlobalFunction( GENSS_TrivialOp,
  function( p, x )
    return p;
  end );

InstallMethod( StabilizerChain, "for a group object", [ IsGroup ],
  function( grp )
    return StabilizerChain( grp, rec() );
  end );

InstallMethod(VerifyStabilizerChainMC, 
  "for a stabilizer chain and a positive integer",
  [ IsStabilizerChainByOrb, IsInt ],
  function( S, nrels )
    local i,x;

    Info(InfoGenSS,2,"Doing randomized verification...");
    i := 0; 
    while i < nrels do
        i := i + 1;
        if IsBound(S!.opt.RandomElmFunc) then
            x := S!.opt.RandomElmFunc();
        else
            x := Next(S!.opt.pr);
        fi;
        if AddGeneratorToStabilizerChain(S,x) then
            Info( InfoGenSS, 2, "Verification found error ... ",
                  "new size ", Size(S) );
            i := 0;
        fi;
    od;
  end );

InstallMethod( StabilizerChain, "for a group object and a record", 
  [ IsGroup, IsRecord ],
  function( grp, opt )
    # Computes a stabilizer chain for the group grp
    # Possible options:
    #   random:     compatibility to StabChain, works as there
    #   ErrorBound: rational giving the error bound, probability of an
    #               error will be proven to be lower than this
    #               if given, this sets VerifyElements and
    #                                   DeterministicVerification
    #   VerifyElements: number of random elements for verification
    #   DeterministicVerification: flag, whether to do or not
    #                              (not yet implemented)
    #   Base:       specify a known base, this can either be the 
    #               StabilizerChain of a known supergroup or a list of
    #               points, if "BaseOps" is not given, it is either
    #               set to OnPoints if Projective is not set or false,
    #               or is set to OnLines if Projective is set to true,
    #               if Base is set
    #   BaseOps:    a list of operation functions for the base points
    #   Projective: if set to true indicates that the matrices given
    #               as generators are to be thought as projective
    #               elements, that is, the whole StabilizerChain will
    #               be one for a projective group!
    #   StrictlyUseCandidates: if set to true exactly the points in
    #               opt.cand will be used as base points and no other
    #               points from previous orbits are used first
    #               this is set when Base was specified
    #   Reduced:    if set to true no orbits of length 1 will occur
    #               (except for the trivial group), is true by default
    #   Cand:       initializer for cand, which are base points
    #               candidates
    #               must be a record with components "points", "ops",
    #               "used", the latter indicates the largest index
    #               that has already been used. "used" is automatically
    #               set to 0 if not set.
    #   TryShortOrbit: Number of tries for the short orbit finding alg.
    #   StabGenScramble,
    #   StabGenScrambleFactor,
    #   StabGenAddSlots,
    #   StabGenMaxDepth:   parameters for product replacer for generating
    #                      random elements
    #   VeryShortOrbLimit: when looking for short orbits try a random
    #                      vector and enumerate its orbit until this limit
    #   
    #   ... to be continued
    #
    local S,cand,i,pr,prob,x,gens,SS;

    # First a few preparations, then we delegate to GENSS_StabilizerChainInner:

    # Add some default options:
    if (HasSize(grp) and not(IsBound(opt.Projective) and opt.Projective))
       or IsBound(opt.Size) then
        if not(IsBound(opt.ImmediateVerificationElements)) then
            opt.ImmediateVerificationElements := 0;
        fi;
    fi;
    if IsBound(opt.Projective) and opt.Projective then
        opt.IsOne := GENSS_IsOneProjective;
    elif not(IsBound(opt.IsOne)) then
        opt.IsOne := IsOne;
    fi;
    # Now opt.IsOne is set to a function to check whether or not a group
    # element is equal to the identity.
    GENSS_CopyDefaultOptions(GENSS,opt);

    # Check for the identity group:
    gens := GeneratorsOfGroup(grp);
    if Length(gens) = 0 or 
       ForAll(gens,opt.IsOne) then
        # Set up a trivial stabilizer chain record:
        S := GENSS_CreateStabChainRecord(false,gens,1,1,GENSS_TrivialOp,
                                         rec( points := [], ops := [],
                                              used := 0),opt);
        Enumerate(S!.orb);
        S!.orb!.gensi := List(S!.orb!.gens,x->x^-1);
        S!.proof := true;
        S!.trivialgroup := true;
        if (not(IsBound(opt.Projective)) or
            opt.Projective = false) and
           IsIdenticalObj(opt.IsOne,IsOne) then
            SetStoredStabilizerChain(grp,S);
        fi;
        return S;
    fi;
    
    # Setup a random element generator for the whole group and store
    # it in the opt record:
    opt.pr := ProductReplacer( gens,
                      rec( scramble := opt.StabGenScramble,
                           scramblefactor := opt.StabGenScrambleFactor,
                           addslots := opt.StabGenAddSlots,
                           maxdepth := opt.StabGenMaxDepth ));

    # Old style error probability for compatibility:
    if IsBound(opt.random) then
        if opt.random = 0 then
            opt.VerifyElements := 0;
        elif opt.random = 1000 then
            opt.DeterministicVerification := true;
            Info(InfoGenSS,1,"Warning: Deterministic verification not yet ",
                 "implemented!");
        else
            prob := 1/2;
            opt.VerifyElements := 1;
            while prob * (1000-opt.random) >= 1 do
                prob := prob / 2;
                opt.VerifyElements := opt.VerifyElements + 1;
            od;
        fi;
    fi;
    # The new style error bounds:
    if IsBound(opt.ErrorBound) then
        prob := 1/2;
        opt.VerifyElements := 1;
        while prob >= opt.ErrorBound do
            prob := prob / 2;
            opt.VerifyElements := opt.VerifyElements + 1;
        od;
    fi;
                
    # Find base point candidates:
    if IsBound(opt.Base) then
        if IsStabilizerChain(opt.Base) then
            cand := GENSS_DeriveCandidatesFromStabChain(opt.Base);
        else  # directly take the base points:
            cand := rec( points := opt.Base, used := 0, points2 := [],
                         ops2 := [] );
            if not(IsBound(opt.BaseOps)) then
                # Let's guess the ops:
                if IsBound(opt.Projective) and opt.Projective = true then
                    cand.ops := ListWithIdenticalEntries(Length(opt.Base),
                                                         OnLines);
                else
                    cand.ops := ListWithIdenticalEntries(Length(opt.Base),
                                                         OnPoints);
                fi;
            else
                cand.ops := opt.BaseOps;
            fi;
        fi;
        opt.StrictlyUseCandidates := true;
    elif IsBound(opt.Cand) then
        cand := opt.Cand;
        if not(IsBound(cand.used)) then
            cand.used := 0;
        fi;
        cand.points2 := [];
        cand.ops2 := [];
    else
        # Otherwise try different things later using generic methods:
        cand := rec( points := [], ops := [], used := 0 );
    fi;
    if not(IsBound(opt.StrictlyUseCandidates)) then
        opt.StrictlyUseCandidates := false;
    fi;
    if HasSize(grp) and not(opt.Projective) then
        S := GENSS_StabilizerChainInner(GeneratorsOfGroup(grp), Size(grp), 1,
                                         cand, opt, false);
    elif IsBound(opt.Size) then
        S := GENSS_StabilizerChainInner(GeneratorsOfGroup(grp), opt.Size, 1,
                                         cand, opt, false);
    else
        S := GENSS_StabilizerChainInner(GeneratorsOfGroup(grp), false, 1, 
                                         cand, opt, false);
    fi;
    if IsString(S) then return S; fi;

    # Do we already have a proof?
    if S!.proof then
        if not(IsBound(opt.Projective)) or opt.Projective = false then
            SetStoredStabilizerChain(grp,S);
        fi;
        return S;
    fi;
    if opt.VerifyElements = 0 then return S; fi;

    Info(InfoGenSS,2,"Current size found: ",Size(S));
    # Now a possible verification phase:
    if S!.size <> false then   # we knew the size in advance
        Info(InfoGenSS,2,"Doing verification via known size...");
        while Size(S) < S!.size do
            Info(InfoGenSS,2,"Known size not reached, throwing in a random ",
                 "element...");
            if IsBound(opt.RandomElmFunc) then
                x := opt.RandomElmFunc();
            else
                x := Next(opt.pr);
            fi;
            if AddGeneratorToStabilizerChain(S,x) then
                Info( InfoGenSS, 2, "Increased size to ",Size(S) );
            fi;
        od;
        S!.proof := true;
        if (not(IsBound(opt.Projective)) or opt.Projective = false) and
           IsIdenticalObj(opt.IsOne,IsOne) then
            SetStoredStabilizerChain(grp,S);
        fi;
    else
        # Do some verification here:
        VerifyStabilizerChainMC(S,opt.VerifyElements);
    fi;
    # Now clean up the random element pools to save memory:
    SS := S;
    while SS <> false do
        if IsBound(SS!.randpool) and Length(SS!.randpool) > 0 then
            SS!.randpool := [];
        fi;
        SS := SS!.stab;
    od;
    return S;
  end );

InstallMethod( AddGeneratorToStabilizerChain,
  "for a stabilizer chain and a new generator",
  [ IsStabilizerChain and IsStabilizerChainByOrb, IsObject ],
  function( S, el )
    # Increases the set represented by S by the generator el.
    local SS, r, n, pr, i, newstrongnr;
    if IsBound(S!.trivialgroup) and S!.trivialgroup then
        if S!.IsOne(el) then
            return false;
        fi;
        SS := StabilizerChain(Group(el),S!.opt);
        if IsString(SS) then return SS; fi;
        for n in NamesOfComponents(SS) do
            S!.(n) := SS!.(n);
        od;
        Unbind(S!.trivialgroup);
        return true;
    fi;

    r := SiftGroupElement( S, el );
    # if this is one then el is already contained in the stabilizer chain
    if r.isone then     # already in the group!
        return false;
    fi;
    # Now there remain two cases:
    #  (1) the sift stopped somewhere and we have to add a generator there
    #  (2) the sift ran all through the chain and the element still was not
    #      the identity, then we have to prolong the chain
    if r.S <> false then   # case (1)
        SS := r.S;
        Info( InfoGenSS, 2, "Adding new generator to stabilizer chain ",
              "in layer ", SS!.layer, "..." );
        Add(SS!.stronggens,r.rem);
        Add(SS!.layergens,Length(SS!.stronggens));
        AddGeneratorsToOrbit(SS!.orb,[r.rem]);
        Add(SS!.orb!.gensi,r.rem^-1);
        newstrongnr := Length(SS!.stronggens);
        Info( InfoGenSS, 4, "Entering orbit enumeration layer ",SS!.layer,
              "..." );
        repeat
            Enumerate(SS!.orb,S!.opt.OrbitLengthLimit);
            if not(IsClosed(SS!.orb)) then
                if S!.opt.FailInsteadOfError then
                    return "Orbit too long, increase S!.opt.OrbitLengthLimit";
                else
                    Error("Orbit too long, increase S!.opt.OrbitLengthLimit!");
                fi;
            fi;
        until IsClosed(SS!.orb);
        Info( InfoGenSS, 4, "Done orbit enumeration layer ",SS!.layer,"..." );
        SS!.proof := false;
    else   # case (2)
        # Note that we do not create a pr instance here for one
        # generator, this will be done later on as needed...
        SS := r.preS;
        newstrongnr := Length(SS!.stronggens)+1;  # r.rem will end up there !
        SS!.stab := GENSS_StabilizerChainInner([r.rem],false,
                           SS!.layer+1,SS!.cand, SS!.opt, SS );
        if IsString(SS!.stab) then return SS!.stab; fi; 
        SS := SS!.stab;
    fi;
    # Now we have added a new generator (or a new layer) at layer SS,
    # the new gen came from layer S (we were called here, after all),
    # thus we have to check, whether all the orbits between S (inclusively)
    # and SS (exclusively) are also closed under the new generator r.rem,
    # we add it to all these orbits, thereby also making the Schreier trees
    # shallower:
    while S!.layer < SS!.layer do
        Info(InfoGenSS,2,"Adding new generator to orbit in layer ",S!.layer);
        Add(S!.layergens,newstrongnr);
        AddGeneratorsToOrbit(S!.orb,[r.rem]);
        Add(S!.orb!.gensi,r.rem^-1);
        S := S!.stab;
    od;
    # Finally, we have to add it to the product replacer!
    AddGeneratorToProductReplacer(S!.opt!.pr,r.rem);
    return true;
  end );

InstallMethod( SiftGroupElement, "for a stabilizer chain and a group element",
  [ IsStabilizerChain and IsStabilizerChainByOrb, IsObject ],
  function( S, x )
    local o,p,po,preS,r,isone;
    isone := S!.IsOne;
    preS := false;
    while S <> false do
        o := S!.orb;
        if IsObjWithMemory(x) then
          p := o!.op(o[1],x!.el);
        else
          p := o!.op(o[1],x);
        fi;
        po := Position(o,p);
        if po = fail then   # not in current stabilizer
            return rec( isone := false, rem := x, S := S, preS := preS );
        fi;
        # Now sift through Schreier tree:
        while po > 1 do
            x := x * S!.orb!.gensi[o!.schreiergen[po]];
            po := o!.schreierpos[po];
        od;
        preS := S;
        S := S!.stab;
    od;
    r := rec( rem := x, S := false, preS := preS, isone := isone(x) );
    return r;
  end );

InstallMethod( SiftGroupElementSLP, 
  "for a stabilizer chain and a group element",
  [ IsStabilizerChain and IsStabilizerChainByOrb, IsObject ],
  function( S, x )
    local preS, nrstrong, slp, o, p, po, r, isone;
    preS := false;
    isone := S!.IsOne;
    nrstrong := Length(S!.stronggens);
    slp := [];     # will be reversed in the end
    while S <> false do
        o := S!.orb;
        p := o!.op(o[1],x);
        po := Position(o,p);
        if po = fail then   # not in current stabilizer
            return rec( isone := false, rem := x, S := S, preS := preS,
                        slp := fail );
        fi;
        # Now sift through Schreier tree:
        while po > 1 do
            x := x * S!.orb!.gensi[o!.schreiergen[po]];
            Add(slp,1);
            Add(slp,S!.layergens[o!.schreiergen[po]]);
            po := o!.schreierpos[po];
        od;
        preS := S;
        S := S!.stab;
    od;
    r := rec( rem := x, S := false, preS := preS, isone := isone(x) );
    if r.isone then
        if Length(slp) = 0 then   # element was the identity!
            r.slp := StraightLineProgramNC([[1,0]],nrstrong);
        else
            r.slp := StraightLineProgramNC(
                           [slp{[Length(slp),Length(slp)-1..1]}],nrstrong);
        fi;
    else
        r.slp := fail;
    fi;
    return r;
  end );

InstallMethod( StrongGenerators, "for a stabilizer chain",
  [ IsStabilizerChain and IsStabilizerChainByOrb ],
  function( S )
    return S!.stronggens;
  end );

InstallMethod( NrStrongGenerators, "for a stabilizer chain",
  [ IsStabilizerChain and IsStabilizerChainByOrb ],
  function( S )
    return Length(S!.stronggens);
  end );

InstallMethod( ForgetMemory, "for a stabilizer chain",
  [ IsStabilizerChain and IsStabilizerChainByOrb ],
  function( S )
    ForgetMemory(S!.stronggens);
    while S <> false do
        ForgetMemory(S!.orb);
        ForgetMemory(S!.orb!.gensi);
        S := S!.stab;
    od;
  end );

InstallMethod( AddNormalizingGenToLayer,
  "for a stabilizer chain, a group element, and a prime number",
  [ IsStabilizerChain and IsStabilizerChainByOrb, IsObject, IsPosInt ],
  function( S, x, p )
    # This is a very specialised function which is not officially documented.
    # It is used for the matrix PCGS code where a stabiliser chain is
    # built up using a known base in a generator by generator fashion
    # where the generators are the generators of the PCGS. Thus a lot
    # is known about them.
    # This function assumes that x is a new generator normalising the
    # group generated by the previous generators in the layer described
    # by S. So in particular, S is not necessarily the top of the 
    # stabiliser chain but the layer where actually something happens.
    # Namely, since it is known that the new generator generates
    # a group which is p times as big as the previous one (p a prime)
    # it is known that the orbit in the given layer will become
    # p times as big and we can enumerate it relatively cheaply.
    local lmp,o,oldnrgens;
    o := S!.orb;
    oldnrgens := Length(o!.gens);
    Add(o!.gens,x);
    Add(o!.gensi,x^-1);
    if IsPermOnIntOrbitRep(o) then
        lmp := LargestMovedPoint(o!.gens);
        if lmp > Length(o!.tab) then
            Append(o!.tab,ListWithIdenticalEntries(lmp-Length(o!.tab),0));
        fi;
    fi;
    ResetFilterObj(o,IsClosed);
    o!.pos := 1;
    o!.genstoapply := [oldnrgens+1..Length(o!.gens)];
    repeat
        Enumerate(o,S!.opt.OrbitLengthLimit);
        if not(IsClosed(o)) then
            if S!.opt.FailInsteadOfError then
                return "Orbit too long, increase S!.opt.OrbitLengthLimit";
            else
                Error("Orbit too long, increase S!.opt.OrbitLengthLimit!");
            fi;
        fi;
    until IsClosed(S!.orb);
    o!.genstoapply := [1..Length(o!.gens)];

    # Now fix up the stabilizer chain record:
    if not x in S!.stronggens then
      Add(S!.stronggens,x);
      Add(S!.layergens,Length(S!.stronggens));
    fi;
    Unbind(S!.size);   # whatever we knew before, we know no longer
 
  end );

InstallMethod( GENSS_CreateSchreierGenerator, 
  "for a stabilizer chain, a position in the first orbit and a gen number",
  [ IsStabilizerChain and IsStabilizerChainByOrb, IsPosInt, IsPosInt ],
  function( S, i, j )
    local o,p,w1,w2;
    o := S!.orb;
    p := Position(o,o!.op(o[i],o!.gens[j]));
    if o!.schreierpos[p] = i and o!.schreiergen[p] = j then
        return fail;
    fi;
    w1 := TraceSchreierTreeForward(o,i);
    w2 := TraceSchreierTreeBack(o,p);
    return [w1,j,w2];
  end );

InstallGlobalFunction( GENSS_Prod,
  function( gens, word )
    if Length(word) = 0 then 
        return gens[1]^0; 
    else
        return Product(gens{word});
    fi;
  end );
  
InstallGlobalFunction( VerifyStabilizerChainTC,
  function( S ) Error("Currently not functional!"); return fail; end );
##    function( S )
##      local Grels,Hrels,Prels,MakeSchreierGens,ct,f,gens,gensi,i,j,k,l,li,max,
##            newpres,nrgens,nrschr,o,ords,pres,sb,sgs,slp,subgens,v,w,x,
##            cosetnrlimitfactor;
##  
##      allstrong := function(S)
##        st := Set(S!.layergens);
##        while S!.stab <> false do
##            S := S!.stab;
##            UniteSet(st,S!.layergens);
##        od;
##        return st;
##      end;
##      if S!.stab <> false then
##          pres := VerifyStabilizerChainTC(S!.stab);
##          if IsList(pres) then return pres; fi;
##          strongbelow := allstrong(S!.stab);
##      else
##          pres := StraightLineProgram([[]],0);
##          strongbelow := [];
##      fi;
##      strong := allstrong(S);
##      Info(InfoGenSS,1,"Verifying stabilizer chain in layer ",S!.layer);
##  
##      # First create a few Schreier generators:
##      sgs := [];
##      i := 1;
##      j := 1;
##      o := S!.orb;
##      nrgens := Length(o!.gens);
##      MakeSchreierGens := function(n)
##          local sg;
##          Info(InfoGenSS,3,"Creating ",n," Schreier generators...");
##          while Length(sgs) < n and
##                i <= Length(o) do
##              sg := GENSS_CreateSchreierGenerator(S,i,j);
##              j := j + 1;
##              if j > nrgens then
##                  j := 1;
##                  i := i + 1;
##              fi;
##              if sg <> fail then
##                  Add(sgs,sg);
##              fi;
##          od;
##      end;
##  
##      nrschr := S!.opt.NumberSchreierGens;
##      MakeSchreierGens(nrschr);
##      f := FreeGroup(Length(strong));
##      gens := GeneratorsOfGroup(f);
##      gensi := List(gens,x->x^-1);
##      subgens := gens{List(strongbelow,i->Position(strong,i))};
##      Hrels := ResultOfStraightLineProgram(pres,subgens);
##      if S!.opt.Projective then
##          ords := List([1..nrgens],i->ProjectiveOrder(o!.gens[i]));
##      else
##          ords := List([1..nrgens],i->Order(o!.gens[i]));
##      fi;
##      Prels := List([1..nrgens],i->gens[i+sb]^ords[i]);
##      Grels := [];
##      cosetnrlimitfactor := 4;
##      while true do   # will be left by return eventually
##          for k in [Length(Grels)+1..Length(sgs)] do
##              Grels[k] := GENSS_Prod(gens,sgs[k][1]+sb) * gens[sgs[k][2]+sb] * 
##                          GENSS_Prod(gensi,sgs[k][3]+sb);
##              x := GENSS_Prod(o!.gens,sgs[k][1]) * o!.gens[sgs[k][2]] * 
##                   GENSS_Prod(o!.gensi,sgs[k][3]);
##              if S!.stab <> false then
##                  slp := SiftGroupElementSLP(S!.stab,x);
##                  if not(slp.isone) then
##                      return [fail,S!.layer];
##                  fi;
##                  Grels[k] := Grels[k] / ResultOfStraightLineProgram(slp.slp,
##                                                                     subgens);
##                  sgs[k][4] := slp.slp;
##              else
##                  if not(S!.IsOne(x)) then
##                      return [fail,S!.layer];
##                  fi;
##                  sgs[k][4] := false;
##              fi;
##          od;
##          Info(InfoGenSS,2,"Doing coset enumeration with limit ",
##               cosetnrlimitfactor*Length(o));
##          ct := CosetTableFromGensAndRels(gens,Concatenation(Prels,Hrels,Grels),
##                     subgens:max := cosetnrlimitfactor*Length(o),silent);
##          if ct = fail then   # did not close!
##              cosetnrlimitfactor := QuoInt(cosetnrlimitfactor*3,2);
##              Info(InfoGenSS,2,"Coset enumeration did not finish!");
##          #Error(1);
##              if nrschr > Length(sgs) # or
##                 # nrschr > S!.opt.MaxNumberSchreierGens 
##                 then   # we are done!
##                  # Something is wrong!
##                  return [fail, S!.layer];
##              fi;
##          else
##              Info(InfoGenSS,2,"Coset enumeration found ",Length(ct[1]),
##                   " cosets.");
##          #Error(2);
##              if Length(ct[1]) = Length(o) then
##                  # Verification is OK, now build a presentation:
##                  l := GeneratorsWithMemory(
##                         ListWithIdenticalEntries(S!.nrstrong,()));
##                  li := List(l,x->x^-1);
##                  newpres := ResultOfStraightLineProgram(pres,
##                                     l{[1..S!.strongbelow]});
##                  for k in [1..nrgens] do
##                      Add(newpres,l[k+sb]^ords[k]);
##                  od;
##                  for k in [1..Length(sgs)] do
##                      if sgs[k][4] <> false then
##                          Add(newpres,
##                              GENSS_Prod(l,sgs[k][1]+sb)*l[sgs[k][2]+sb]*
##                              GENSS_Prod(li,sgs[k][3]+sb)*
##                              ResultOfStraightLineProgram(sgs[k][4],l)^-1);
##                      else
##                          Add(newpres,GENSS_Prod(l,sgs[k][1]+sb)*l[sgs[k][2]+sb]*
##                                      GENSS_Prod(li,sgs[k][3]+sb));
##                      fi;
##                  od;
##                  Info(InfoGenSS,2,"Found presentation for layer ",S!.layer,
##                       " using ",Length(newpres)," relators.");
##                  return SLPOfElms(newpres);
##              fi;
##          fi;
##          nrschr := QuoInt(nrschr*4,2);
##          MakeSchreierGens(nrschr);
##      od;
##    end);


GENSS_CosetTableFromGensAndRelsInit :=
function(fgens,grels,fsgens,limit)
    local   next,  prev,            # next and previous coset on lists
            firstFree,  lastFree,   # first and last free coset
            firstDef,   lastDef,    # first and last defined coset
            table,                  # columns in the table for gens
            rels,                   # representatives of the relators
            relsGen,                # relators sorted by start generator
            subgroup,               # rows for the subgroup gens
            i, gen, inv,            # loop variables for generator
            g,                      # loop variable for generator col
            rel,                    # loop variables for relation
            p, p1, p2,              # generator position numbers
            app,                    # arguments list for 'MakeConsequences'
            j,                      # integer variable
            length, length2,        # length of relator (times 2)
            cols,
            nums,
            l,
            nrdef,                  # number of defined cosets
            nrmax,                  # maximal value of the above
            nrdel,                  # number of deleted cosets
            nrinf;                  # number for next information message

    # give some information
    Info( InfoFpGroup, 3, "    defined deleted alive   maximal");
    nrdef := 1;
    nrmax := 1;
    nrdel := 0;
    nrinf := 1000;

    # define one coset (1)
    firstDef  := 1;  lastDef  := 1;
    firstFree := 2;  lastFree := limit;

    # make the lists that link together all the cosets
    next := [ 2 .. limit + 1 ];  next[1] := 0;  next[limit] := 0;
    prev := [ 0 .. limit - 1 ];  prev[2] := 0;

    # compute the representatives for the relators
    rels := RelatorRepresentatives( grels );

    # make the columns for the generators
    table := [];
    for gen  in fgens  do
        g := ListWithIdenticalEntries( limit, 0 );
        Add( table, g );
        if not ( gen^2 in rels or gen^-2 in rels ) then
            g := ListWithIdenticalEntries( limit, 0 );
        fi;
        Add( table, g );
    od;

    # make the rows for the relators and distribute over relsGen
    relsGen := RelsSortedByStartGen( fgens, rels, table, true );

    # make the rows for the subgroup generators
    subgroup := [];
    for rel  in fsgens  do
      #T this code should use ExtRepOfObj -- its faster
      # cope with SLP elms
      if IsStraightLineProgElm(rel) then
        rel:=EvalStraightLineProgElm(rel);
      fi;
        length := Length( rel );
        length2 := 2 * length;
        nums := [ ]; nums[length2] := 0;
        cols := [ ]; cols[length2] := 0;

        # compute the lists.
        i := 0;  j := 0;
        while i < length do
            i := i + 1;  j := j + 2;
            gen := Subword( rel, i, i );
            p := Position( fgens, gen );
            if p = fail then
                p := Position( fgens, gen^-1 );
                p1 := 2 * p;
                p2 := 2 * p - 1;
            else
                p1 := 2 * p - 1;
                p2 := 2 * p;
            fi;
            nums[j]   := p1;  cols[j]   := table[p1];
            nums[j-1] := p2;  cols[j-1] := table[p2];
        od;
        Add( subgroup, [ nums, cols ] );
    od;

    # make the structure that is passed to 'MakeConsequences'
    app := [ table, next, prev, relsGen, subgroup, 
             firstFree, lastFree, firstDef, lastDef, 0, 0 ];

    # we do not want minimal gaps to be marked in the coset table
    app[12] := 0;

    return rec( nrdef := nrdef, nrmax := nrmax, nrdel := nrdel, nrinf := nrinf,
                app := app, closed := false, table := table, limit := limit,
                fgens := fgens );
end;

GENSS_DoToddCoxeter := function( r )
    local app,fgens,firstDef,firstFree,gen,i,inv,lastDef,lastFree,next,nrdef,
          nrdel,nrinf,nrmax,prev,relsGen,subgroup,table;

    fgens := r.fgens;
    nrdef := r.nrdef;
    nrmax := r.nrmax;
    nrdel := r.nrdel;
    nrinf := r.nrinf;
    app := r.app;
    table := app[1];
    next := app[2];
    prev := app[3];
    relsGen := app[4];
    subgroup := app[5];
    firstFree := app[6];
    lastFree := app[7];
    firstDef := app[8];
    lastDef := app[9];

    # run over all the cosets:
    while firstDef <> 0  do

        # run through all the rows and look for undefined entries
        for i  in [ 1 .. Length( table ) ]  do
            gen := table[i];

            if gen[firstDef] <= 0  then

                inv := table[i + 2*(i mod 2) - 1];

                # if necessary expand the table
                if firstFree = 0  then
                    app[6] := firstFree;
                    app[7] := lastFree;
                    app[8] := firstDef;
                    app[9] := lastDef;
                    r.nrdef := nrdef;
                    r.nrmax := nrmax;
                    r.nrdel := nrdel;
                    r.nrinf := nrinf;
                    return fail;
                fi;

                # update the debugging information
                nrdef := nrdef + 1;
                if nrmax <= firstFree  then
                    nrmax := firstFree;
                fi;

                # define a new coset
                gen[firstDef]   := firstFree;
                inv[firstFree]  := firstDef;
                next[lastDef]   := firstFree;
                prev[firstFree] := lastDef;
                lastDef         := firstFree;
                firstFree       := next[firstFree];
                next[lastDef]   := 0;

                # set up the deduction queue and run over it until it's empty
                app[6] := firstFree;
                app[7] := lastFree;
                app[8] := firstDef;
                app[9] := lastDef;
                app[10] := i;
                app[11] := firstDef;
                nrdel := nrdel + MakeConsequences( app );
                firstFree := app[6];
                lastFree := app[7];
                firstDef := app[8];
                lastDef  := app[9];

                # give some information
                if nrinf <= nrdef+nrdel then
                    Info( InfoFpGroup, 3, "\t", nrdef, "\t", nrinf-nrdef,
                          "\t", 2*nrdef-nrinf, "\t", nrmax );
                    nrinf := ( Int(nrdef+nrdel)/1000 + 1 ) * 1000;
                fi;

            fi;
        od;

        firstDef := next[firstDef];
    od;

    Info( InfoFpGroup, 2, "\t", nrdef, "\t", nrdel, "\t", nrdef-nrdel, "\t",
          nrmax );

    # separate pairs of identical table columns.                  
    for i in [ 1 .. Length( fgens ) ] do                             
        if IsIdenticalObj( table[2*i-1], table[2*i] ) then
            table[2*i] := StructuralCopy( table[2*i-1] );       
        fi;                                                                
    od;

    # standardize the table
    StandardizeTable( table );

    # return the success result
    r.closed := true;
    return true;
end;

GENSS_ToddCoxeterExpandLimit := function(r,newlimit)
    local app,g,l,limit,next,prev,table;
    if newlimit <= r.limit then return r; fi;
    app := r.app;
    table := app[1];
    next := app[2];
    prev := app[3];
    limit := r.limit;
    next[newlimit] := 0;
    prev[newlimit] := newlimit-1;
    for g  in table  do g[newlimit] := 0;  od;
    for l  in [ limit+2 .. newlimit-1 ]  do
        next[l] := l+1;
        prev[l] := l-1;
        for g  in table  do g[l] := 0;  od;
    od;
    next[limit+1] := limit+2;
    prev[limit+1] := 0;
    for g  in table  do g[limit+1] := 0;  od;
    app[6] := limit+1;   # this is firstFree
    r.limit := newlimit;
    app[7] := newlimit;     # this is lastFree
end;

GENSS_TCAddRelators := function(r,newrels)
  local i,relsGen;
  newrels := RelatorRepresentatives(newrels);
  newrels := RelsSortedByStartGen( r.fgens, newrels, r.table, true );
  relsGen := r.app[4];
  for i in [1..Length(relsGen)] do
      Append(relsGen[i],newrels[i]);
  od;
end;


##  VerifyStabilizerChainTC5 := 
##    function( S )
##      local FindTwoWords,Grels,Hrels,Prels,allgens,cosetlimit,done,el,f,
##            gens,gensi,hom,k,l,li,newpres,newrel,nrcosets,nrgens,o,opgens,
##            ords,pres,r,rels,sb,stronggens,strongi,subgens,tc,words;
##  
##      if S!.stab <> false then
##          pres := VerifyStabilizerChainTC5(S!.stab);
##          if IsList(pres) then return pres; fi;
##      else
##          pres := StraightLineProgram([[]],0);
##      fi;
##      Info(InfoGenSS,1,"Verifying stabilizer chain in layer ",S!.layer);
##  
##      o := S!.orb;
##      nrgens := Length(o!.gens);
##      sb := S!.strongbelow;
##              
##      f := FreeGroup(sb+nrgens);
##      gens := GeneratorsOfGroup(f);
##      gensi := List(gens,x->x^-1);
##      allgens := 0*[1..2*Length(gens)];
##      allgens{[1,3..2*Length(gens)-1]} := gens;
##      allgens{[2,4..2*Length(gens)]} := gensi;
##      subgens := gens{[1..S!.strongbelow]};
##      Hrels := ResultOfStraightLineProgram(pres,subgens);
##      if S!.opt.Projective then
##          ords := List([1..nrgens],i->ProjectiveOrder(o!.gens[i]));
##      else
##          ords := List([1..nrgens],i->Order(o!.gens[i]));
##      fi;
##      Prels := List([1..nrgens],i->gens[i+sb]^ords[i]);
##      Grels := [];
##      stronggens := StrongGenerators(S);
##      strongi := List(stronggens,x->x^-1);
##      opgens := 0*[1..2*Length(stronggens)];
##      opgens{[1,3..2*Length(stronggens)-1]} := stronggens;
##      opgens{[2,4..2*Length(stronggens)]} := strongi;
##  
##      # Now start up a coset enumeration:
##      cosetlimit := Maximum(QuoInt(7 * Length(o),6),Length(o)+5);
##      Info(InfoGenSS,2,"Starting coset enumeration with limit ",
##           cosetlimit," and ",Length(Hrels),
##           "+",Length(Prels),"+",Length(Grels)," relations...");
##      rels := Concatenation(Hrels,Prels);
##      tc := GENSS_CosetTableFromGensAndRelsInit(gens,rels,subgens,cosetlimit);
##      done := GENSS_DoToddCoxeter(tc);
##          
##      FindTwoWords := function(o,opgens,table)
##          local TraceWord,cosets,cosetsrevtab,i,j,new,nrcosets,pt,pts,
##                ptsrev,schgen,schpt,w1,w2,x,y;
##          nrcosets := Length(table[1]);
##          cosets := [1];
##          cosetsrevtab := 0*[1..nrcosets];
##          cosetsrevtab[1] := 1;
##          pts := [o[1]];
##          ptsrev := 0*[1..Length(o)];
##          ptsrev[1] := 1;
##          schpt := [fail];    # the Schreier tree
##          schgen := [fail];
##          i := 1;
##          TraceWord := function(pos)
##              local w;
##              w := [];
##              while pos > 1 do
##                  Add(w,schgen[pos]);
##                  pos := schpt[pos];
##              od;
##              return Reversed(w);
##          end;
##          while i <= Length(cosets) do
##              for j in [1..Length(opgens)] do
##                  x := table[j][cosets[i]];
##                  if x <> 0 then   # image is defined:
##                      if cosetsrevtab[x] = 0 then   # not visited
##                          Add(cosets,x);
##                          new := Length(cosets);
##                          cosetsrevtab[x] := new;
##                          schpt[new] := i;
##                          schgen[new] := j;
##                          pt := o!.op(pts[i],opgens[j]);
##                          y := Position(o,pt);
##                          if ptsrev[y] = 0 then
##                              Add(pts,pt);
##                              ptsrev[y] := new;
##                          else
##                              # We have reached a new coset by a word that
##                              # maps the starting point of the orbit to the 
##                              # same point as the one of another coset!
##                              w1 := TraceWord(ptsrev[y]);
##                              w2 := TraceWord(new);
##                              return [w1,w2];
##                          fi;
##                      fi;
##                  fi;
##              od;
##              i := i + 1;
##          od;
##          Error("Bad, this should never have been reached!");
##          return fail;
##      end;
##  
##      while true do   # will be left by return eventually
##          if done = true then
##              nrcosets := Length(tc.table[1]);
##              Info(InfoGenSS,2,"Coset enumeration found ",nrcosets," cosets.");
##              if nrcosets = Length(o) then
##                  # Verification is OK, now build a presentation:
##                  l := GeneratorsWithMemory(
##                         ListWithIdenticalEntries(S!.nrstrong,()));
##                  newpres := ResultOfStraightLineProgram(pres,
##                                     l{[1..S!.strongbelow]});
##                  for k in [1..nrgens] do
##                      Add(newpres,l[k+sb]^ords[k]);
##                  od;
##                  hom := GroupHomomorphismByImagesNC(f,Group(l),gens,l);
##                  for k in Grels do
##                      Add(newpres,ImageElm(hom,k));
##                  od;
##                  Info(InfoGenSS,2,"Found presentation for layer ",S!.layer,
##                       " using ",Length(newpres)," relators.");
##                  return SLPOfElms(newpres);
##              elif nrcosets < Length(o) then
##                  Error("This cannot possibly have happened!");
##                  return [fail,S!.layer];
##              else   # nrcosets > Length(o)
##                  Info(InfoGenSS,2,"Too many cosets, we must have forgotten ",
##                       "another relation!");
##                  Error("This cannot possibly have happened2!");
##                  return [fail,S!.layer];
##              fi;
##          fi;
##          # Now we have to find another relation, we do a breadth-first
##          # search through the already defined cosets to find two cosets
##          # that are still different but ought to be equal because the
##          # corresponding orbit points are equal:
##          words := FindTwoWords(o,opgens,tc.table);
##          el := EvaluateWord(opgens,words[1])/EvaluateWord(opgens,words[2]);
##          r := SiftGroupElementSLP(S,el);
##          if not(r.isone) then
##              # Error, we found a new stabilizer element!
##              return [fail,S!.layer,el];
##          fi;
##          newrel := [(EvaluateWord(allgens,words[1])
##                      /EvaluateWord(allgens,words[2]))
##                     / ResultOfStraightLineProgram(r.slp,gens)];
##          GENSS_TCAddRelators(tc,newrel);
##          Add(Grels,newrel[1]);
##          if Length(words[1]) > 0 then
##              tc.app[10] := words[1][1];
##          else
##              # More difficult:
##              words := ExtRepOfObj(newrel[1]);
##              if words[2] > 0 then
##                  tc.app[10] := 2*words[1]-1;
##              else
##                  tc.app[10] := 2*words[1];
##              fi;
##          fi;
##          #Print("<\c");
##          #for i in [1..tc.limit] do
##          #    for j in [1..Length(allgens)] do
##          #        if tc.table[j][i] <> 0 then
##          #            tc.app[11] := i;
##          #            tc.app[10] := j;
##          #            tc.nrdel := tc.nrdel + MakeConsequences( tc.app );
##          #        fi;
##          #    od;
##          #od;
##          tc.app[11] := 1;
##          tc.nrdel := tc.nrdel + MakeConsequences( tc.app );
##          #Print("-\c");
##          done := GENSS_DoToddCoxeter(tc);
##          #Print(">\c");
##          if Length(Grels) mod 100 = 0 then
##              #Print("\n");
##              Info(InfoGenSS,2,"Currently using ",Length(Hrels),"+",
##                   Length(Prels),"+",Length(Grels)," relations.");
##          fi;
##      od;
##    end;
##  
##  VerifyStabilizerChainMax := 
##    function( S )
##      local bl,count,d,gens,i,invtab,j,k,o,p,r,res,s,w1,w2,x,xi,y,isone;
##  
##      isone := S!.IsOne;
##      if S!.stab <> false then
##          res := VerifyStabilizerChainMax(S!.stab);
##          if res <> true then return res; fi;
##      fi;
##      Info(InfoGenSS,1,"Verifying stabilizer chain in layer ",S!.layer,
##           " (orbit of length ",Length(S!.orb),")");
##  
##      count := 0;
##      gens := ShallowCopy(Set(S!.orb!.gens));
##      invtab := [];
##      k := Length(gens);
##      for i in [1..k] do
##          x := gens[i];
##          xi := x^-1;
##          p := Position(gens,xi);
##          if p = fail then
##              Add(gens,xi);
##              invtab[i] := Length(gens);
##              invtab[Length(gens)] := i;
##          else
##              invtab[i] := p;
##          fi;
##      od;
##      o := Orb(gens,S!.orb[1],S!.orb!.op,rec(schreier := true, log := true));
##      Enumerate(o);
##  
##      if Length(o) <> Length(S!.orb) then
##          Error("something is fishy, orbits do not have the same length");
##          return fail;
##      fi;
##  
##      # Now we have a nice breadth-first orbit such that all inverses of 
##      # generators are again generators.
##      # First check whether the generators fix the first point:
##      for x in gens do
##          if o!.op(o[1],x) = o[1] then
##              count := count + 1;
##              if S!.stab = false then
##                  r := rec( isone := isone(x) );
##              else
##                  r := SiftGroupElement(S!.stab,x);
##              fi;
##              if not(r.isone) then
##                  return [fail,S!.layer,x,"generator"];
##              fi;
##          fi;
##      od;
##  
##      bl := BlistList([1..Length(o)],[]);
##      for d in [1..o!.depth] do
##          Info(InfoGenSS,2,"Testing in depth ",d," (of ",o!.depth,") checking ",
##               o!.depthmarks[d+1]-o!.depthmarks[d]," points (of ",Length(o),")");
##          for i in [o!.depthmarks[d]..o!.depthmarks[d+1]-1] do
##              # These indices contain points in depth d
##              if bl[o!.schreierpos[i]] then
##                  # this subtree does not need to be done!
##                  bl[i] := true;   # mark subtree as done
##              else
##                  x := o[i];
##                  for j in [1..Length(gens)] do
##                      if j <> invtab[o!.schreiergen[i]] then
##                          y := o!.op(x,gens[j]);
##                          p := Position(o,y);
##                          # if p > i then this is a new point, do nothing
##                          if p <= i then
##                              count := count + 1;
##                              w1 := TraceSchreierTreeForward(o,i);
##                              w2 := TraceSchreierTreeForward(o,p);
##                              s := (EvaluateWord(gens,w1)*gens[j])
##                                   /EvaluateWord(gens,w2);
##                              #Print(Length(gens),w1,j,w2,"\n");
##                              if S!.stab = false then
##                                  r := rec( isone := isone(s) );
##                              else
##                                  r := SiftGroupElement(S!.stab,s);
##                              fi;
##                              if not(r.isone) then
##                                  return [fail,S!.layer,s,"schreier gen"];
##                              fi;
##                          fi;
##                          if p < o!.depthmarks[d] then
##                              # this goes up in the tree, this means, if this
##                              # Schreier generator is OK, we do not have to
##                              # look below!
##                              bl[i] := true;
##                              break;
##                          fi;
##                      fi;
##                  od;
##              fi;
##          od;
##      od;
##      Info(InfoGenSS,2,"Have sifted ",count," elements.");
##      return true;
##    end;

#############################################################################
# The following operations apply to stabilizer chains:
#############################################################################

InstallMethod( Size, "for a stabilizer chain",
  [ IsStabilizerChain and IsStabilizerChainByOrb ],
  function( S )
    local size;
    size := 1;
    while S <> false do
        size := size * Length(S!.orb);
        S := S!.stab;
    od;
    return size;
  end );

InstallMethod( \in, "for a group element and a stabilizer chain",
  [ IsObject, IsStabilizerChain and IsStabilizerChainByOrb ],
  function( el, S )
    local r;
    r := SiftGroupElement(S,el);
    if r.isone then
        return true;
    else
        return false;
    fi;
  end );
    
GENSS_VIEWDEPTH := 0;  # to please the interpreter
InstallMethod( ViewObj, "for a stabilizer chain",
  [ IsStabilizerChain and IsStabilizerChainByOrb ],
  function( S )
    local i;
    if not(IsBound(GENSS_VIEWDEPTH)) then
        GENSS_VIEWDEPTH := 0;
    else
        GENSS_VIEWDEPTH := GENSS_VIEWDEPTH + 1;
    fi;
    for i in [1..GENSS_VIEWDEPTH] do Print(" "); od;
    Print("<stabchain size=",Size(S)," orblen=",Length(S!.orb),
          " layer=",S!.layer," SchreierDepth=",S!.orb!.depth,">");
    if S!.stab <> false then
        Print("\n");
        ViewObj(S!.stab);
    fi;
    GENSS_VIEWDEPTH := GENSS_VIEWDEPTH - 1;
    if GENSS_VIEWDEPTH < 0 then
        Unbind(GENSS_VIEWDEPTH);
    fi;
  end );
Unbind(GENSS_VIEWDEPTH);

InstallMethod( BaseStabilizerChain,
  "generic method for stabilizer chains",
  [IsStabilizerChain],
  function( S )
    local SS,pts,ops;
    pts := [];
    ops := [];
    SS := S;
    while SS <> false do
        Add(pts,SS!.orb[1]);
        Add(ops,SS!.orb!.op);
        SS := SS!.stab;
    od;
    return rec( points := pts, ops := ops );
  end );

InstallMethod( SiftBaseImage,
  "generic method for genss stabilizer chains",
  [IsStabilizerChain, IsList],
  function(S,bi)
    local l, SS, i, o, pos;
    l := Length(bi);
    SS := S;
    i := 1;
    while i <= Length(bi) do
        o := SS!.orb;
        pos := Position(o,bi[i]);
        if pos = fail then
            return false;
        fi;
        while pos > 1 do
            if o!.memorygens then
                bi{[i..l]} := GENSS_MapBaseImage(bi{[i..l]},
                                o!.gensi[o!.schreiergen[pos]]!.el,SS);
            else
                bi{[i..l]} := GENSS_MapBaseImage(bi{[i..l]},
                                o!.gensi[o!.schreiergen[pos]],SS);
            fi;
            pos := o!.schreierpos[pos];
        od;
        if o[1] <> bi[i] then
            Error("this should not have happened, tell the authors");
        fi;
        i := i + 1;
        SS := SS!.stab;
    od;
    return true;
  end );

InstallMethod( IsProved, "for a stabilizer chain",
  [ IsStabilizerChain and IsStabilizerChainByOrb ],
  function( S )
    return S!.proof;
  end );

InstallMethod( StabChainOp, "for a permutation group and a stabilizer chain",
  [ IsPermGroup, IsStabilizerChain and IsStabilizerChainByOrb ],
  function( g, S )
    local base;
    if HasSize(g) and Size(g) <> Size(S) then
        Error("known size of group does not match stabiliser chain");
        return fail;
    else
        SetSize(g,Size(S));
    fi;
    base := BaseStabilizerChain(S);
    if not(ForAll(base.points,IsInt) and ForAll(base.ops,x->x=OnPoints)) then
        return StabChainOp(g);
    fi;
    return StabChainOp(g,rec( base := base.points, knownBase := base.points, 
                              size := Size(S) ));
  end );

InstallGlobalFunction( GENSS_FindGensStabilizer,
  function( S, pt, opt )
    # Assumes that S is a correct stabilizer chain for the group generated by
    # its gens (in S!.orb!.gens) and that pt lies in the first orbit S!.orb.
    # Finds an SLP to express generators of the stabilizer of pt in
    # the group in terms of the gens.
    # Assumes that the group and the first stabilizer in the chain are 
    # non-trivial.
    local gens,g,pos,mgens,word,cosetrep,invgens,pr,stabgens,size,stabsize,
          randel,newpt,ptpos,ptcosetrep,el,SS,stab,i;
    gens := S!.orb!.gens;
    g := GroupWithGenerators(gens);
    SetSize(g,Size(S));
    pos := Position(S!.orb,pt);
    if pos = fail then
        Error("point pt must lie in first orbit");
        return fail;
    fi;
    if not(IsBound(opt.scramble)) then opt.scramble := 30; fi;
    if not(IsBound(opt.scramblefactor)) then opt.scramblefactors := 0; fi;
    if not(IsBound(opt.maxdepth)) then opt.maxdepth := 200; fi;
    if not(IsBound(opt.addslots)) then 
        opt.addslots := Maximum(0,5-Length(gens)); 
    fi;
    # Give the gens a (new) memory:
    if IsObjWithMemory(gens[1]) then
        mgens := GeneratorsWithMemory(List(gens,x->x!.el));
    else
        mgens := GeneratorsWithMemory(gens);
    fi;
    # FIXME: use GENSS_Prod
    if pos = 1 then
        cosetrep := mgens[1]^0;
    else
        word := TraceSchreierTreeForward(S!.orb,pos);
        cosetrep := Product(mgens{word});
    fi;
    invgens := List(mgens,x->x^-1);

    # Set up the product replacer:
    pr := ProductReplacer( mgens, opt );
    stabgens := [];   # here we collect the stabilizer generators
    size := 1;
    stabsize := Size(S!.stab);   # this has to exist!
    repeat
        randel := Next(pr);
        newpt := S!.orb!.op(pt,randel!.el);
        ptpos := Position(S!.orb,newpt);
        word := TraceSchreierTreeBack(S!.orb,ptpos);
        if Length(word) > 0 then
            ptcosetrep := Product(invgens{word});
            el := randel * ptcosetrep;
        else
            el := randel;
        fi;
        if pos <> 1 then
            el := el * cosetrep;
        fi;
        # now el is an element in the stabilizer of pt 
        if S!.IsOne(el) then
            Info(InfoGenSS,3,"Found trivial stabilizer element.");
        else
            Add(stabgens,el);
            stab := GroupWithGenerators(stabgens);
            SS := StabilizerChain(stab,rec( Base := S, IsOne := S!.IsOne ));
            Info(InfoGenSS,1,"Have group size ",Size(SS)," (of ",
                 stabsize,")");
            if Size(SS) = size then
                Remove(stabgens);
            else
                size := Size(SS);
            fi;
        fi;
    until size = stabsize;
    # Try leaving out the first generators found:
    i := 1;
    repeat
        Info(InfoGenSS,1,"Need ",Length(stabgens)+1-i," generators.");
        i := i + 1;
        if i < Length(stabgens) then
            stab := Group(stabgens{[i..Length(stabgens)]});
            SS := StabilizerChain(stab,rec(Base := S, IsOne := S!.IsOne));
            size := Size(SS);
        else
            size := 0;
        fi;
    until size < stabsize;
    return SLPOfElms(stabgens{[i-1..Length(stabgens)]});
  end );

InstallGlobalFunction( GENSS_FindShortGensStabilizerOld,
  function( S, pt )
    # Assumes that S is a correct stabilizer chain for the group generated by
    # its gens (in S!.orb!.gens) and that pt lies in the first orbit S!.orb.
    # Finds an SLP to express generators of the stabilizer of pt in
    # the group in terms of the gens.
    # Assumes that the group and the first stabilizer in the chain are 
    # non-trivial.
    local gens,g,pos,mgens,word,cosetrep,invgens,pr,stabgens,size,stabsize,
          randel,newpt,ptpos,ptcosetrep,el,SS,stab,i,j;
    gens := S!.orb!.gens;
    g := GroupWithGenerators(gens);
    SetSize(g,Size(S));
    pos := Position(S!.orb,pt);
    if pos = fail then
        Error("point pt must lie in first orbit");
        return fail;
    fi;
    # Give the gens a (new) memory:
    if IsObjWithMemory(gens[1]) then
        mgens := GeneratorsWithMemory(List(gens,x->x!.el));
    else
        mgens := GeneratorsWithMemory(gens);
    fi;
    if pos = 1 then
        cosetrep := mgens[1]^0;
    else
        word := TraceSchreierTreeForward(S!.orb,pos);
        cosetrep := Product(mgens{word});
    fi;
    invgens := List(mgens,x->x^-1);

    # Set up the search:
    pr := ShallowCopy(mgens);
    i := 1;   # counts points
    j := 1;   # counts gens
    stabgens := [];   # here we collect the stabilizer generators
    size := 1;
    stabsize := Size(S!.stab);   # this has to exist!
    repeat
        Add(pr,pr[i]*mgens[j]);
        j := j + 1; 
        if j > Length(mgens) then
            j := 1;
            i := i + 1;
        fi;
        randel := pr[Length(pr)];
        newpt := S!.orb!.op(pt,randel!.el);
        ptpos := Position(S!.orb,newpt);
        word := TraceSchreierTreeBack(S!.orb,ptpos);
        if Length(word) > 0 then
            ptcosetrep := Product(invgens{word});
            el := randel * ptcosetrep;
        else
            el := randel;
        fi;
        if pos <> 1 then
            el := el * cosetrep;
        fi;
        # now el is an element in the stabilizer of pt 
        if S!.IsOne(el) then
            Info(InfoGenSS,2,"Found trivial stabilizer element.");
        else
            Add(stabgens,el);
            stab := GroupWithGenerators(stabgens);
            SS := StabilizerChain(stab,rec( Base := S, IsOne := S!.IsOne ));
            Info(InfoGenSS,1,"Have group size ",Size(SS)," (of ",
                 stabsize,")");
            if Size(SS) = size then
                Remove(stabgens);
            else
                size := Size(SS);
            fi;
        fi;
    until size = stabsize;
    # Try leaving out the first generators found:
    i := 1;
    repeat
        Info(InfoGenSS,1,"Need ",Length(stabgens)+1-i," generators.");
        i := i + 1;
        if i < Length(stabgens) then
            stab := Group(stabgens{[i..Length(stabgens)]});
            SS := StabilizerChain(stab,rec(Base := S, IsOne := S!.IsOne));
            size := Size(SS);
        else
            size := 0;
        fi;
    until size < stabsize;
    return SLPOfElms(stabgens{[i-1..Length(stabgens)]});
  end );

InstallGlobalFunction( GENSS_FindShortGensStabilizer,
  function( gens, pt, op, grpsize, stabsize, S )
    # Assumes that S is a correct stabilizer chain a supergroup of the
    # group g generated by gens. pt and op is an action for g.
    # grpsize is the Size of g and stabsize is the size of the stabilizer.
    # Finds an SLP to express generators of the stabilizer of pt in
    # g in terms of the gens.
    # Assumes that g and the orbit of pt under g are non-trivial.
    local SS,el,g,i,invgens,j,mgens,newpt,orb,pr,ptcosetrep,ptpos,randel,size,
          stab,stabgens,word;
    g := GroupWithGenerators(gens);
    Info(InfoGenSS,2,"Enumerating orbit of length ",grpsize/stabsize);
    orb := Orb(gens,pt,op,rec(schreier := true,stabsizebound := stabsize));
    Enumerate(orb);
    # Give the gens a (new) memory:
    if IsObjWithMemory(gens[1]) then
        mgens := GeneratorsWithMemory(List(gens,x->x!.el));
    else
        mgens := GeneratorsWithMemory(gens);
    fi;
    invgens := List(mgens,x->x^-1);

    # Set up the search:
    pr := ShallowCopy(mgens);
    i := 1;   # counts points
    j := 1;   # counts gens
    stabgens := [];   # here we collect the stabilizer generators
    size := 1;
    repeat
        Add(pr,pr[i]*mgens[j]);
        j := j + 1; 
        if j > Length(mgens) then
            j := 1;
            i := i + 1;
        fi;
        randel := pr[Length(pr)];
        newpt := op(pt,randel!.el);
        ptpos := Position(orb,newpt);
        word := TraceSchreierTreeBack(orb,ptpos);
        if Length(word) > 0 then
            ptcosetrep := Product(invgens{word});
            el := randel * ptcosetrep;
        else
            el := randel;
        fi;
        # now el is an element in the stabilizer of pt 
        if S!.IsOne(el) then
            Info(InfoGenSS,3,"Found trivial stabilizer element.");
        else
            Add(stabgens,el);
            stab := GroupWithGenerators(stabgens);
            SS := StabilizerChain(stab,rec( Base := S, IsOne := S!.IsOne ));
            Info(InfoGenSS,1,"Have group size ",Size(SS)," (of ",
                 stabsize,")");
            if Size(SS) = size then
                Remove(stabgens);
            else
                size := Size(SS);
            fi;
        fi;
    until size >= stabsize;
    if size > stabsize then
        Error("Something is wrong, stabsize limit was too small");
        return fail;
    fi;
    # Try leaving out the first generators found:
    i := 1;
    Info(InfoGenSS,1,"Need ",Length(stabgens)," generators.");
    while i < Length(stabgens) do
       stab := Group(stabgens{Concatenation([1..i-1],[i+1..Length(stabgens)])});
       SS := StabilizerChain(stab,rec(Base := S, IsOne := S!.IsOne));
       if Size(SS) < stabsize then
           i := i + 1;   # keep generator
       else
           Remove(stabgens,i);
           Info(InfoGenSS,1,"Need ",Length(stabgens)," generators.");
       fi;
    od;
    return SLPOfElms(stabgens);
  end );

InstallGlobalFunction( SLPChainStabilizerChain,
  function( S, gens )
    # S a stabilizer chain assumed to be correct for the group g generated
    # by generators gens.
    # Returns a list of straight line programs expressing successively
    # the stabilizers in the chain, each in terms of the generators of the
    # previous, the first in terms of gens.
    local l,ll,slp;
    l := [];
    ll := [Size(S)];
    while S!.stab <> false do
        Info(InfoGenSS,1,"Working on group with size ",Size(S),"...");
        slp := GENSS_FindShortGensStabilizer( 
             gens, S!.orb[1], S!.orb!.op, Size(S), Size(S!.stab), S );
        Add(l,slp);
        Add(ll,Size(S!.stab));
        gens := ResultOfStraightLineProgram(slp,gens);
        Info(InfoGenSS,1,"Found SLP with ",
             Length(LinesOfStraightLineProgram(slp))," lines to compute ",
             Length(gens)," generators.");
        S := S!.stab;
    od;
    return rec(slps := l,sizes := ll);
  end );

InstallGlobalFunction( GENSS_MakeIterRecord,
  function( S )
    local SS,Slist,it;
    Slist := [S];
    SS := S!.stab;
    while SS <> false do
        Add(Slist,SS);
        SS := SS!.stab;
    od;
    it := rec( NextIterator := GENSS_GroupNextIterator,
               IsDoneIterator := GENSS_GroupIsDoneIterator,
               ShallowCopy := GENSS_GroupShallowCopy,
               pos := ListWithIdenticalEntries( Length( S!.base ), 1 ),
               Slist := Slist,
               cache := ListWithIdenticalEntries( Length(S!.base), 
                                                  One(S!.orb!.gens[1]) ),
               one := One(S!.orb!.gens[1]),
               lens := List(Slist,x->Length(x!.orb)) );
    return it;
  end );

InstallMethod( GroupIteratorByStabilizerChain, "for a stabilizer chain",
  [ IsStabilizerChain ],
  function( S )
    return IteratorByFunctions(GENSS_MakeIterRecord(S));
  end );

InstallGlobalFunction( GENSS_GroupNextIterator,
  function( iter )
    local done,i,j,res,word;
    if iter!.pos[1] > iter!.lens[1] then return fail; fi;
    res := iter!.cache[Length(iter!.cache)];
    # Now step forwards:
    i := Length(iter!.Slist);
    repeat
        iter!.pos[i] := iter!.pos[i] + 1;
        if iter!.pos[i] > iter!.lens[i] then
            iter!.pos[i] := 1;
            i := i - 1;
            done := false;
        else
            done := true;
        fi;
    until done or i = 0;
    if i = 0 then 
        iter!.pos[1] := iter!.lens[1]+1; 
        return res; 
    fi;   # we are done
    word := TraceSchreierTreeForward(iter!.Slist[i]!.orb,iter!.pos[i]);
    iter!.cache[i] := Product(iter!.Slist[i]!.orb!.gens{word});
    if i > 1 then iter!.cache[i] := iter!.cache[i] * iter!.cache[i-1]; fi;
    for j in [i+1..Length(iter!.Slist)] do
        iter!.cache[j] := iter!.cache[j-1];
    od;
    return res;
  end );

InstallGlobalFunction( GENSS_GroupIsDoneIterator,
  function( iter )
    return iter!.pos[1] > iter!.lens[1];
  end );

InstallGlobalFunction( GENSS_GroupShallowCopy,
  function( iter )
    return GENSS_MakeIterRecord( iter!.Slist[1] );
  end );

#############################################################################
# We can store a stabilizer chain in the attribute
# StoredStabilizerChain, then the following methods for groups apply:
#############################################################################

InstallMethod( SetStabilizerChain, "for a group and a stabilizer chain",
  [IsGroup, IsStabilizerChain],
  function(g,S)
    if IsIdenticalObj(S!.IsOne,IsOne) and
       HasSize(g) and Size(g) <> Size(S) then
      Error("you try to set a stabilizer chain for the wrong group");
      return;
    fi;
    SetStoredStabilizerChain(g,S);
  end );

InstallMethod( Size, "for a group with a stored stabilizer chain",
  [IsGroup and HasStoredStabilizerChain],
  function(g)
    return Size(StoredStabilizerChain(g));
  end );

InstallMethod( Size, "for a perm. group with a stored stabilizer chain",
  [IsPermGroup and HasStoredStabilizerChain],
  function(g)
    return Size(StoredStabilizerChain(g));
  end );

InstallMethod( Size, "for a group with a stored stabilizer chain",
  [IsGroup and HasStoredStabilizerChain and IsHandledByNiceMonomorphism],
  function(g)
    return Size(StoredStabilizerChain(g));
  end );

InstallMethod( \in, "for a group elm and a group with stored stabilizer chain",
  [IsPerm, IsPermGroup and HasStoredStabilizerChain], 1,
  function(x, g)
    local S,r;
    S := StoredStabilizerChain(g);
    r := SiftGroupElement(S,x);
    return r.isone;
  end );

InstallMethod( \in, "for a group elm and a group with stored stabilizer chain",
  [IsObject, IsMatrixGroup and HasStoredStabilizerChain and 
             IsHandledByNiceMonomorphism],
  function(x, g)
    local S,r;
    S := StoredStabilizerChain(g);
    r := SiftGroupElement(S,x);
    return r.isone;
  end );

InstallMethod( \in, "for a group elm and a group with stored stabilizer chain",
  [IsObject, IsGroup and HasStoredStabilizerChain],
  function(x, g)
    local S,r;
    S := StoredStabilizerChain(g);
    r := SiftGroupElement(S,x);
    return r.isone;
  end );

InstallOtherMethodWithRandomSource( Random, "for a random source and a stabilizer chain",
  [ IsRandomSource, IsStabilizerChainByOrb ],
  function(rs, S)
    local x,pos,w;
    x := One(S!.orb!.gens[1]);
    while S <> false do
        pos := Random(rs, 1,Length(S!.orb));
        w := TraceSchreierTreeForward(S!.orb,pos);
        x := GENSS_Prod(S!.orb!.gens,w) * x;
        S := S!.stab;
    od;
    return x;
  end );

InstallMethodWithRandomSource( Random, "for a random source and a group with a stored stabilizer chain",
  [ IsRandomSource, IsGroup and HasStoredStabilizerChain ],
  function(rs, g) return Random(rs, StoredStabilizerChain(g)); end );

InstallMethodWithRandomSource( Random, "for a random source and a permgroup with a stored stabilizer chain",
  [ IsRandomSource, IsPermGroup and HasStoredStabilizerChain ], 10,
  function(rs, g) return Random(rs, StoredStabilizerChain(g)); end );

InstallMethodWithRandomSource( Random, "for a random source and a matrixgroup with a stored stabilizer chain",
  [ IsRandomSource, IsMatrixGroup and IsHandledByNiceMonomorphism and 
    HasStoredStabilizerChain ],
  function(rs, g) return Random(rs, StoredStabilizerChain(g)); end );

InstallMethod( SizeMC, "for a group and an error bound",
  [IsGroup, IsRat],
  function( G, err )
    local S;
    S := StabilizerChain(G,rec( ErrorBound := err ));
    return Size(S);
  end );

InstallMethod( SizeMC, "for a permutation group and an error bound, genss",
  [IsGroup and IsPermGroup, IsRat], 1,
  function( G, err )
    local S;
    S := StabilizerChain(G,rec( ErrorBound := err ));
    return Size(S);
  end );

InstallGlobalFunction( GENSS_ImageElm,
  function( data, x )
    local r;
    r := SiftGroupElementSLP(data!.Sg,x);
    if not(r.isone) then return fail; fi;
    return ResultOfStraightLineProgram(r.slp,data!.stronggims);
  end );

InstallGlobalFunction( GENSS_PreImagesRepresentative,
  function( data, x )
    local r;
    r := SiftGroupElementSLP(data!.Si,x);
    if not(r.isone) then return fail; fi;
    return ResultOfStraightLineProgram(r.slp,data!.strongipre);
  end );

InstallGlobalFunction( GroupHomomorphismByImagesNCStabilizerChain,
  function( g, h, images, opt1, opt2 )
    local Sg,Si,data,gm,im,slpstrongg,slpstrongi,strongg,stronggims,
          strongi,strongipre;
    gm := GroupWithMemory(g);
    Sg := StabilizerChain(gm,opt1);
    strongg := StrongGenerators(Sg);
    slpstrongg := SLPOfElms(strongg);
    ForgetMemory(Sg);
    stronggims := ResultOfStraightLineProgram(slpstrongg,images);
    im := GroupWithMemory(images);
    Si := StabilizerChain(im,opt2);
    strongi := StrongGenerators(Si);
    slpstrongi := SLPOfElms(strongi);
    ForgetMemory(Si);
    strongipre := ResultOfStraightLineProgram(slpstrongi,GeneratorsOfGroup(g));
    data := rec( Sg := Sg, stronggims := stronggims,
                 Si := Si, strongipre := strongipre,
                 slpstrongg := slpstrongg, slpstrongi := slpstrongi );
    return GroupHomByFuncWithData( g, h, GENSS_ImageElm, false,
                                   GENSS_PreImagesRepresentative, data );
  end );

InstallMethod( ORB_StabilizerChainKnownSize,
  "GENSS method for arbitrary groups",
  [IsGroup,IsPosInt],
  function(g,size)
    if HasStoredStabilizerChain(g) and
       size = Size(StoredStabilizerChain(g)) then
        return StoredStabilizerChain(g);
    fi;
    return StabilizerChain(g,rec( Size := size ));
  end );

InstallMethod( ORB_BaseStabilizerChain,
  "GENSS method for arbitary groups",
  [IsStabilizerChain],
  BaseStabilizerChain );

InstallMethod( ORB_StabilizerChainKnownBase,
  "GENSS method for arbitrary groups",
  [IsGroup,IsObject],
  function(g,base)
    if HasStoredStabilizerChain(g) then
        return StoredStabilizerChain(g);
    fi;
    if base = fail then
        return StabilizerChain( g );
    else
        return StabilizerChain( g, rec( Cand := ShallowCopy(base) ) );
    fi;
  end );

InstallMethod( ORB_SizeStabilizerChain,
  "GENSS method for arbitrary groups",
  [IsStabilizerChain], Size );

InstallMethod( ORB_IsWordInStabilizerChain,
  "GENSS method for arbitrary groups",
  [IsList,IsList,IsList,IsStabilizerChain],
  function(word, gens, gensi, S)
    local x, b, ops, i;
    if Size(S) = 1 then
        x := ORB_ApplyWord(gens[1]^0,word,gens,gensi,OnRight);
        return S!.IsOne(x);
    fi;
    b := BaseStabilizerChain(S);
    ops := b.ops;
    b := b.points;
    for i in [1..Length(word)] do
        if word[i] > 0 then
            b := List([1..Length(b)],j->ops[j](b[j],gens[word[i]]));
        else
            b := List([1..Length(b)],j->ops[j](b[j],gensi[-word[i]]));
        fi;
    od;
    return SiftBaseImage(S,b);
  end );

InstallMethod( ORB_IsElementInStabilizerChain,
  "GENSS method for arbitrary groups",
  [IsObject, IsStabilizerChain],
  function(el, S)
    return el in S;
  end );


#############################################################################
# The following methods are about methods to compute stabilisers:
#############################################################################

InstallMethod( Stab, "add empty options record",
  [IsGroup, IsObject, IsFunction],
  function( g, x, op )
    return Stab(g,x,op,rec());
  end );

InstallMethod( Stab, "add empty options record",
  [IsList, IsObject, IsFunction],
  function( l, x, op )
    return Stab(l,x,op,rec());
  end );

InstallMethod( Stab, "create group from list of generators",
  [IsList, IsObject, IsFunction, IsRecord],
  function( l, x, op, opt )
    return Stab(GroupWithGenerators(l),x,op,opt);
  end );

# Now the real one: 

InstallMethod( Stab, "by Orb orbit enumeration",
  [IsGroup, IsObject, IsFunction, IsRecord],
  function( g, x, op, opt )
    local S,count,el,errorprob,found,gens,i,j,limit,memperpt,nrrand,o,
          pat,pos,pr,res,stab,stabchain,stabel,stabgens,stabsizeest,w1,w2,y;
    GENSS_CopyDefaultOptions(GENSS,opt);
    if HasStoredStabilizerChain(g) then
      S := StoredStabilizerChain(g);
      if IsIdenticalObj(S!.orb!.op,op) and x in S!.orb then
        if S!.stab = false then
            y := rec( stab := TrivialSubgroup(g), size := 1, proof := true );
            y.stabilizerchain := StabilizerChain(y.stab);
            return y;
        fi;
        pos := Position(S!.orb,x);
        w1 := TraceSchreierTreeForward(S!.orb,pos);
        y := GENSS_Prod(S!.orb!.gens,w1);
        stab := Group(List(S!.stab!.orb!.gens,a->y^-1*a*y));
        return rec( stab := stab, size := Size(S!.stab),
                    stabilizerchain := StabilizerChain(stab,rec( Base := S)),
                    proof := true );
      fi;
    fi;
    # Now we have to do it ourselves:
    if not(IsBound(opt.ErrorBound)) then   # we are working deterministically
        if not(HasSize(g)) then
            # We are basically stuffed, unless we want to check all
            # Schreier generators of an orbit!
            Error("I do not want to check all Schreier generators, ",
                  "need size of group or ErrorBound option!");
            return fail;
        fi;
    else
        if opt.ErrorBound < opt.StabAssumeCompleteLimit then
            opt.StabAssumeCompleteLimit := opt.ErrorBound;
        fi;
    fi;
    # Now we either know the group size or work Monte Carlo:
    errorprob := 1;
    limit := opt.StabInitialLimit;
    pat := opt.StabInitialPatience;
    o := Orb(g,x,op,rec( report := opt.Report, 
                         treehashsize := opt.InitialHashSize,
                         schreier := true ) );
    stabgens := [];
    stabsizeest := 1;
    pr := ProductReplacer(GeneratorsOfGroup(g),
                rec( scramble := opt.StabScramble, 
                     scramblefactor := opt.StabScrambleFactor,
                     addslots := opt.StabAddSlots,
                     maxdepth := opt.StabMaxDepth ));
    gens := GeneratorsOfGroup(g);
    # Now go through a cycle of orbit enumeration and stabilizer generation:
    repeat
        if not(IsClosed(o)) then
            if HasSize(g) and limit > QuoInt(Size(g),stabsizeest*2)+1 then
                limit := QuoInt(Size(g),stabsizeest*2)+2;
            fi;
            Info(InfoGenSS,2,"Enumerating orbit with limit ",limit);
            Enumerate(o,limit);
            if IsClosed(o) then
                Info(InfoGenSS,2,"Orbit closed, size is ",Length(o));
            fi;
        fi;
        if errorprob < opt.StabAssumeCompleteLimit then 
            if HasSize(g) and 
               2 * Length(o) * stabsizeest > Size(g) then
                # Done!
                #stab := Subgroup(g,stabgens);
                if Length(stabgens) > 0 then
                    stab := Group(stabgens);
                    SetParent(stab,g);
                    SetSize(stab,stabsizeest);
                    SetStabilizerChain(stab,stabchain);
                else
                    stab := TrivialSubgroup(g);
                    stabchain := StabilizerChain(stab);
                fi;
                return rec( stab := stab, size := stabsizeest, 
                            stabilizerchain := stabchain,
                            proof := true );
            fi;
            limit := 2*limit;
            if not IsClosed(o) then continue; fi;
        fi;
        count := 0;
        found := false;
        repeat
            el := Next(pr);
            i := 1;
            while i <= Length(o) and not(found) do
                y := op(o[i],el);
                j := Position(o,y);
                if j <> fail then 
                    found := true; 
                else
                    i := i + 1;
                fi;
            od;
            count := count + 1;
        until found or count >= pat;
        if not(IsClosed(o)) then
            if not(found) then
                limit := QuoInt(3*limit,2);
            else
                if count < 10 then
                    limit := limit + 100;
                else
                    limit := QuoInt(3*limit,2);
                fi;
            fi;
        fi;
        pat := QuoInt(pat*5,4);
        if found then   # Have a potential stabiliser element:
            Info(InfoGenSS,3,"Found a potential stabilising element...");
            w1 := TraceSchreierTreeForward(o,i);
            w2 := TraceSchreierTreeForward(o,j);
            stabel := EvaluateWord(gens,w1)*el/EvaluateWord(gens,w2);
            if IsOne(stabel) then
                    errorprob := errorprob / 2;
                    Info(InfoGenSS,3,"... which was the identity.");
            else
                Add(stabgens,stabel);
                if Length(stabgens) < 2 then
                    Info(InfoGenSS,3,"Waiting for second stabiliser element.");
                else      # Length(stabgens) >= 2 then
                    if Length(stabgens) = 2 then
                        Info(InfoGenSS,2,"Estimating stabilizer...");
                        stabchain := StabilizerChain(Group(stabgens));
                        errorprob := 1;
                    else
                        res := AddGeneratorToStabilizerChain(stabchain,stabel);
                        if not(res) then
                            Remove(stabgens,Length(stabgens));
                            errorprob := errorprob / 2;
                            Info(InfoGenSS,2,"Error probablity now < ",
                                 errorprob);
                        else
                            Info(InfoGenSS,2,
                                 "Added generator to stabilizer...");
                            VerifyStabilizerChainMC(stabchain,10);
                            errorprob := 1;
                        fi;
                    fi;
                    if Size(stabchain) > stabsizeest then
                        stabsizeest := Size(stabchain);
                        Info(InfoGenSS,2,"New stabilizer estimate: ",
                             stabsizeest);
                    else
                        Info(InfoGenSS,2,"Stabiliser estimate unchanged.");
                    fi;
                    if HasSize(g) and
                       2 * Length(o) * stabsizeest > Size(g) then
                        # Done!
                        if Length(stabgens) > 0 then
                            stab := Group(stabgens);
                            SetParent(stab,g);
                            SetSize(stab,stabsizeest);
                            SetStabilizerChain(stab,stabchain);
                        else
                            stab := TrivialSubgroup(g);
                            stabchain := StabilizerChain(stab);
                        fi;
                        return rec( stab := stab, size := stabsizeest, 
                                    stabilizerchain := stabchain,
                                    proof := true );
                    fi;
                    if IsBound(opt.ErrorBound) and
                       errorprob < opt.ErrorBound then
                        #stab := Subgroup(g,stabgens);
                        if Length(stabgens) > 0 then
                            stab := Group(stabgens);
                            SetParent(stab,g);
                        else
                            stab := TrivialSubgroup(g);
                            stabchain := StabilizerChain(stab);
                        fi;
                        return rec( stab := stab, size := stabsizeest,
                                    stabilizerchain := stabchain,
                                    proof := false );
                    fi;
                fi;
            fi;
        else   # no element found
            Info(InfoGenSS,3,"No stabiliser element found!");
        fi;
    until Length(o) > opt.StabOrbitLimit;
    return fail;
  end );

InstallMethod( StabMC, "add empty options record",
  [IsGroup, IsObject, IsFunction],
  function( g, x, op )
    return StabMC(g,x,op,rec());
  end );

InstallMethod( StabMC, "add empty options record",
  [IsList, IsObject, IsFunction],
  function( l, x, op )
    return StabMC(l,x,op,rec());
  end );

InstallMethod( StabMC, "create group from list of generators",
  [IsList, IsObject, IsFunction, IsRecord],
  function( l, x, op, opt )
    return StabMC(GroupWithGenerators(l),x,op,opt);
  end );

InstallMethod( StabMC, "call Stab with errorbound",
  [IsGroup, IsObject, IsFunction, IsRecord],
  function( g, x, op, opt )
    if not(IsBound(opt.ErrorBound)) then
        opt.ErrorBound := 1/1025;
    fi;
    if not(IsBound(opt.DoEstimate)) then
        opt.DoEstimate := 1000;   # try for 1 second
    fi;
    return Stab(g,x,op,opt);
  end );


InstallGlobalFunction( BacktrackSearchStabilizerChainElement,
  function( S,     # a stabilizer chain
            P,     # a property function G -> {true,false}
            g,     # a group element
            pruner )
    # Let G be a group and U being the subgroup defined by S.
    # Does a backtrack search in U using S for an element x of U
    # for which P(xg) is true. g not necessarily in U!
    # pruner is either false or a function taking arguments as seen below.
    
    local cache,gen,i,ii,res,t,w,x,dotree;

    cache := WeakPointerObj([[S!.orb!.gens[1]^0,[]]]);
    for i in [1..Length(S!.orb)] do
        ii := S!.orb!.orbind[i];
        if ii > 1 then
            t := ElmWPObj(cache,S!.orb!.schreierpos[ii]);
            if t <> fail then
                gen := S!.orb!.schreiergen[ii];
                w := EmptyPlist(Length(t[2])+1);
                Append(w,t[2]);
                Add(w,gen);
                t := t[1] * S!.orb!.gens[gen];
            else
                w := TraceSchreierTreeForward(S!.orb,ii);
                t := Product(S!.orb!.gens{w});
            fi;
            SetElmWPObj(cache,ii,[t,w]);
            x := t*g;
        else
            w := [];
            t := S!.orb!.gens[1]^0;
            x := g;
        fi;
        if S!.stab = false then
            if P(x) then 
                return rec( elm := x, vec := [i], word := S!.layergens{w} );
            fi;
        else
            if pruner = false or pruner(S,ii,x,t,w) then
                res := BacktrackSearchStabilizerChainElement(S!.stab,P,x,
                                                             pruner);
                if res <> fail then
                    Add(res.vec,ii,1);
                    res.word := Concatenation(res.word,S!.layergens{w});
                    return res;
                fi;
            fi;
        fi;
    od;
    return fail;
  end );

InstallGlobalFunction( ComputeSuborbitsForStabilizerChain,
  function( S )
    local SS,gens;
    SS := S;
    while SS!.stab <> false do
        gens := StrongGenerators(S){SS!.stab!.layergens};
        SS!.suborbs := FindSuborbits(S!.orb,gens);
        SS := SS!.stab;
    od;
  end );

InstallGlobalFunction( BacktrackSearchStabilizerChainSubgroup,
  function(S,P,pruner) 
    # Let G be a group and U being the subgroup defined by S.
    # Does a backtrack search in U using S for the subgroup H of U
    # with H := {h \in U | P(h)=true}. P must be such that H is 
    # a subgroup of U.
    # pruner is either false or a function taking arguments as seen below.

    local SS,SSS,cache,dotree,gen,i,ii,newgens,o,res,t,w,T;

    cache := WeakPointerObj([[S!.orb!.gens[1]^0,[]]]);
    if S!.stab = false then    # lowest level
        newgens := [];
        o := false;
        for i in [2..Length(S!.orb)] do
            ii := S!.orb!.orbind[i];
            if o = false or not(S!.orb[ii] in o) then
                if ii > 1 then
                    t := ElmWPObj(cache,S!.orb!.schreierpos[ii]);
                    if t <> fail then
                        gen := S!.orb!.schreiergen[ii];
                        w := EmptyPlist(Length(t[2])+1);
                        Append(w,t[2]);
                        Add(w,gen);
                        t := t[1] * S!.orb!.gens[gen];
                    else
                        w := TraceSchreierTreeForward(S!.orb,ii);
                        t := Product(S!.orb!.gens{w});
                    fi;
                    SetElmWPObj(cache,ii,[t,w]);
                else
                    w := [];
                    t := S!.orb!.gens[1]^0;
                fi;
                if P(t) then
                    Add(newgens,t);
                    if Length(newgens) = 1 then
                        o := Orb(ShallowCopy(newgens),S!.orb[1],S!.orb!.op,
                                 rec( treehashsize := 200, schreier := true,
                                      log := true ));
                    else
                        AddGeneratorsToOrbit(o,[t]);
                    fi;
                    Enumerate(o);
                fi;
            fi;
        od;
        if o = false then   # No solution found
            newgens := [S!.orb!.gens[1]^0];
            o := Orb(newgens,S!.orb[1],S!.orb!.op,
                     rec(schreier := true, log := true));
            Enumerate(o);
        fi;
        SSS := rec( stab := false, size := Length(o), base := [o[1]],
                    opt := ShallowCopy(S!.opt), layer := S!.layer,
                    orb := o, stronggens := newgens, 
                    layergens := [1..Length(newgens)], randpool := [], 
                    IsOne := S!.opt.IsOne, proof := true,
                    parentS := false);
        Objectify( StabChainByOrbType, SSS );
        SSS!.opt.pr := ProductReplacer(SSS!.stronggens);
        SSS!.topS := SSS;   # this will be changed further up!
        o!.stabilizerchain := SSS;
        o!.gensi := List(o!.gens,x->x^-1);
        Info(InfoGenSS,2,"Layer ",SSS!.layer," completed, size ",Size(SSS));
        return SSS;
    else   # S!.stab <> false
        # First go down in tree, essentially trying coset rep 1 first:
        SS := BacktrackSearchStabilizerChainSubgroup(S!.stab,P,pruner);
        newgens := [];
        o := false;
        for i in [2..Length(S!.orb)] do
            ii := S!.orb!.orbind[i];
            if o = false or not(S!.orb[ii] in o) then
                if ii > 1 then
                    t := ElmWPObj(cache,S!.orb!.schreierpos[ii]);
                    if t <> fail then
                        gen := S!.orb!.schreiergen[ii];
                        w := EmptyPlist(Length(t[2])+1);
                        Append(w,t[2]);
                        Add(w,gen);
                        t := t[1] * S!.orb!.gens[gen];
                    else
                        w := TraceSchreierTreeForward(S!.orb,ii);
                        t := Product(S!.orb!.gens{w});
                    fi;
                    SetElmWPObj(cache,ii,[t,w]);
                else
                    w := [];
                    t := S!.orb!.gens[1]^0;
                fi;
                if pruner = false or pruner(S,ii,t,t,w) then
                    res := BacktrackSearchStabilizerChainElement(S!.stab,P,t,
                                                                 pruner);
                    if res <> fail then
                      Add(newgens,res.elm);
                      if Length(newgens) = 1 then
                        o := Orb(Concatenation(StrongGenerators(SS),newgens),
                                 S!.orb[1],S!.orb!.op,
                        rec( treehashsize := 
                                    Maximum(100,QuoInt(Length(S!.orb),3)), 
                             schreier := true, log := true ));
                      else
                        AddGeneratorsToOrbit(o,[res.elm]);
                      fi;
                      Enumerate(o);
                    fi;
                fi;
            fi;
        od;
        if o = false then   # No solution found
            o := Orb([S!.orb!.gens[1]^0],S!.orb[1],S!.orb!.op,
                     rec(schreier := true, log := true));
            Enumerate(o);
        fi;
        SSS := rec( stab := SS, size := Length(o)*Size(SS), 
                    base := SS!.base, opt := ShallowCopy(S!.opt), 
                    layer := S!.layer,
                    orb := o, stronggens := SS!.stronggens, 
                    layergens := [1..Length(SS!.stronggens)+Length(newgens)],
                    randpool := [], IsOne := SS!.opt.IsOne, proof := true,
                    parentS := false );
        Add(SSS.base,o[1],1);
        Append(SSS.stronggens,newgens);
        SSS.opt.pr := ProductReplacer(ShallowCopy(SSS.stronggens));
        Objectify( StabChainByOrbType, SSS );
        SS!.parentS := SSS;
        T := SSS;
        while T <> false do
            T!.topS := SSS;
            T := T!.stab;
        od;
        o!.stabilizerchain := SSS;
        o!.gensi := List(o!.gens,x->x^-1);
        Info(InfoGenSS,2,"Layer ",SSS!.layer," completed, size ",Size(SSS));
        return SSS;
    fi;
  end );

InstallMethod( FindShortGeneratorsOfSubgroup, 
  "without option rec or func, permutation group method",
  [ IsPermGroup, IsPermGroup ],
  function(G,U)
    local S; 
    S := StabilizerChain(U);
    return FindShortGeneratorsOfSubgroup(G,U,
             rec( membershiptest :=
                    function(a,b) 
                      return SiftGroupElement(S,a).isone; 
                    end,
                  sizetester :=
                    function(a) 
                      return Size(StabilizerChain(a)); 
                    end ));
  end );

InstallMethod( FindShortGeneratorsOfSubgroup, 
  "without option rec or func, matrix group method",
  [ IsMatrixGroup, IsMatrixGroup ],
  function(G,U)
    local S; 
    S := StabilizerChain(U);
    return FindShortGeneratorsOfSubgroup(G,U,
             rec( membershiptest :=
                    function(a,b) 
                      return SiftGroupElement(S,a).isone; 
                    end,
                  sizetester :=
                    function(a) 
                      return Size(StabilizerChain(a)); 
                    end ));
  end );


[Dauer der Verarbeitung: 0.60 Sekunden, vorverarbeitet 2026-04-27]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge