#############################################################################
##
##  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;
    if IsObjWithMemory(l[1]) then
        ForgetMemory(l);
    fi;
    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 );

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

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

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

InstallMethod( Random, "for a matrixgroup with a stored stabilizer chain",
  [ IsMatrixGroup and IsHandledByNiceMonomorphism and 
    HasStoredStabilizerChain ],
  function(g) return Random(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 );

