Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/matgrp/lib/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 25.7.2025 mit Größe 78 kB image not shown  

SSL recograt.gi   Interaktion und
Portierbarkeitunbekannt

 
#############################################################################
##
#W  recograt.gi                 matgrp package               Alexander Hulpke
##
##
#Y  Copyright (C)  2014-18, Alexander Hulpke
##
##  basic setup for matrix fitting free.
##

SetInfoLevel(InfoRecog,0); # recog will print status messages otherwise

# mod to recog -- rings

# newer recog has some changes to these internal APIs
if not IsBound(RIFac) and IsBound(ImageRecogNode) then
  RIFac := ImageRecogNode;
  RIKer := KernelRecogNode;
fi;

if IsBound(BindRecogMethod) then

BindRecogMethod(FindHomMethodsMatrix, "Nonfield",
"catch matrix groups defined over nonfield rings",
function(ri, G)
  if IsBound(ri!.ring) and not IsBound(ri!.field) then
    Error("hereIAm");
  fi;
  return false;
end);

AddMethod( FindHomDbMatrix, FindHomMethodsMatrix.Nonfield, 5100);


else

FindHomMethodsMatrix.Nonfield := function(ri, G)
  if IsBound(ri!.ring) and not IsBound(ri!.field) then
    Error("hereIAm");
  fi;
  return false;
end;

AddMethod( FindHomDbMatrix, FindHomMethodsMatrix.Nonfield,
  5100, "Nonfield",
          "catch matrix groups defined over nonfield rings" );

fi;

OnSubmoduleCosets:=function(cset,g)
local v;
  return [SiftedVector(cset[2],cset[1]*g),cset[2]];
end;

MakeSubmoduleCosetAction:=function(basis)
  return function(vec,g)
    return SiftedVector(basis,vec*g);
  end;
end;

MakeSubmoduleColineAction:=function(basis)
  return function(vec,g)
  local c;
    vec:=SiftedVector(basis,vec*g);
    c:=PositionNonZero(vec);
    if c<=Length(vec) then
      vec := Inverse( vec[c] ) * vec;
    fi;
    return vec;
  end;
end;

FUNCSPACEHASH:=[];
# mod to genss -- use submodules and quotients for base points

MSSFBPC:=function( grp, opt, ii, parentS ) # owf fct to call easily
local F,dim,orb,orbs,i,fct,
mo,cs,j,k,dims,bas,basc,basinv,nb,lastdim,cand,fcand,sel,limit,trysel,submodule;

  trysel:=function(recsub,recfac)
  local lgens;
    # nor the trivial action
    if ForAny(mo,x->Order(x{sel}{sel})>1) then
    Info(InfoFFMat,2,"range ",sel," have ",Length(cand.points));
      lgens:=List(mo,x->x{sel}{sel});
      fcand:=FindBasePointCandidates(Group(lgens),opt,ii,
        false:Subrecurse:=recsub,Facrecurse:=recfac);
      Info( InfoGenSS, 3, "Subfactor module of range ",sel,", ",Length(fcand.ops),
     " candidates");
      for k in [1..Length(fcand.ops)] do
 if ForAll(lgens,x->fcand.ops[k](fcand.points[k],x)=fcand.points[k]) then
   Info(InfoFFMat,2,"Ignoring fixed element for base");

 elif fcand.ops[k]=OnRight or fcand.ops[k]=OnPoints or fcand.ops[k]=OnLines then
   nb:=fcand.points[k]*bas{sel};
   if lastdim=0 then
     # proper subspace -- just vectors
     Add(cand.points,nb);
     Add(cand.ops,fcand.ops[k]);
   elif true then
     # # action on cosets
     submodule:=SemiEchelonBasis(VectorSpace(F,bas{[1..lastdim]},Zero(bas[1])));
     nb:=SiftedVector(submodule,nb);
     if fcand.ops[k]=OnLines then
       fct:=MakeSubmoduleColineAction(submodule);
       Add(FUNCSPACEHASH,[fct,submodule]);
     else
       fct:=MakeSubmoduleCosetAction(submodule);
       Add(FUNCSPACEHASH,[fct,submodule]);
     fi;
     Add(cand.points,nb);
     Add(cand.ops,fct);
   elif Length(sel)=1 then
     # TODO: 1-dim factor -- need to do cosets
     Info(InfoWarning,1,"Case not yet implemented");
   else
     # subfactor -- take subspace preimage
     nb:=OnSubspacesByCanonicalBasis(Concatenation(bas{[1..lastdim]},[nb]),
    One(grp));
     Add(cand.points,nb);
     Add(cand.ops,OnSubspacesByCanonicalBasis);
   fi;
 elif ForAny(FUNCSPACEHASH,x->x[1]=fcand.ops[k]) then
   fct:=First(FUNCSPACEHASH,x->x[1]=fcand.ops[k]);
   submodule:=SemiEchelonBasis(VectorSpace(F,
        Concatenation(bas{[1..lastdim]},
          BasisVectors(fct[2])*bas{sel})));
          Add(cand.points,fcand.points[k]*bas{sel});
   fct:=MakeSubmoduleCosetAction(submodule);
   Add(FUNCSPACEHASH,[fct,submodule]);
          Add(cand.ops,fct);
    Info(InfoFFMat,2,"ACTPOP");
 elif fcand.ops[k]=OnSubspacesByCanonicalBasis then
   nb:=fcand.points[k]*bas{sel};
   nb:=OnSubspacesByCanonicalBasis(Concatenation(bas{[1..lastdim]},nb),
  One(grp));
   Add(cand.points,nb);
   Add(cand.ops,OnSubspacesByCanonicalBasis);
 else
   Info(InfoWarning,1,"Action not recognized");
 fi;
      od;
      return true;
    else
      return false;
    fi;
  end;

  if IsBound(opt.VeryShortOrbLimit) then
    limit:=2*opt.VeryShortOrbLimit;
  else
    limit:=10^4;
  fi;

  F := DefaultFieldOfMatrixGroup(grp);

  # don't bother in small cases
  if Size(F)^Length(One(grp))<=opt.ShortOrbitsOrbLimit then
    TryNextMethod();
  fi;

  cs:=GeneratorsOfGroup(grp);
  if ForAny(cs,IsObjWithMemory) then
    cs:=Concatenation(Filtered(cs,x->not IsObjWithMemory(x)),
           List(Filtered(cs,x->IsObjWithMemory(x)),
         x->x!.el));
  fi;

  cand:=rec(ops:=[],points:=[],used:=0);
  mo:=GModuleByMats(cs,F);
  dim:=mo.dimension;
  if MTX.IsIrreducible(mo) or Size(F)^dim<=limit then
    TryNextMethod();
  fi;

  # build new basis corresponding to comp ser.
  cs:=MTX.BasesCompositionSeries(mo);
  Info(InfoFFMat,2,"dims=",List(cs,Length));
  dims:=List(cs,Length);
  bas:=[];
  basc:=[];
  for j in [2..Length(cs)] do
    nb:=BaseSteinitzVectors(cs[j],basc);
    Append(bas,nb.factorspace);
    basc:=Concatenation(nb.subspace,nb.factorspace);
    Sort(basc,function(a,b) return PositionNonZero(a)<PositionNonZero(b);end);
  od;
  basinv:=bas^-1;

  mo:=List(mo.generators,x->bas*x*basinv);

  # now step up in sizes indicated by short orbit lengths
  lastdim:=0;
  j:=2;
  if ValueOption("Subrecurse")<>false then
    while j<=Length(dims) and Length(cand.points)<=5 do
      # don't bother is the space is too small
      if (j=Length(dims) and lastdim>0) or
 (j<Length(dims) and Size(F)^(dims[j+1]-lastdim)>limit) then
 sel:=[lastdim+1..dims[j]];
 if trysel(false,true) then
   # we tried a space
   lastdim:=dims[j];
 fi;

      fi;

      j:=j+1;
    od;
  fi;

  if lastdim=0 and ValueOption("Facrecurse")<>false then

    # all but last submodule was trivial -- step down on factors
    j:=Length(dims)-1;
    while j>0 and Length(cand.points)<=5 do
      lastdim:=dims[j]-1;
      if (j>1 and Size(F)^(dim-dims[j-1])>limit) then
 sel:=[lastdim+1..dim];
 if trysel(false,false) then
   j:=0;
 fi;
      fi;
      j:=j-1;
    od;

  fi;

  if Length(cand.points)>0 then return cand; fi;

  # refer to parent
  if IsBound(opt.PCand) then
    # try which ones are short
    sel:=[];
    for i in [1..Length(opt.PCand.points)] do
      orb:=[opt.PCand.points[i]];
      mo:=opt.PCand.ops[i];
      orbs:=Set(orb); # as short, set is fine.
      j:=1;
      while j<=Length(orb) and Length(orb)<=limit do
 for k in GeneratorsOfGroup(grp) do
   cs:=mo(orb[j],k);
   if not cs in orbs then
     Add(orb,cs);
     AddSet(orbs,cs);
   fi;
 od;
 j:=j+1;
      od;
      if Length(orb)<=limit then
 Add(sel,i);
 Add(cand.points,orb[1]);
 Add(cand.ops,mo);
      fi;
    od;
    Info(InfoFFMat,2,"Selected ",Length(sel)," of ",Length(opt.PCand.points)," group basis vectors");
    if Length(cand.points)>0 then return cand; fi;
  fi;

  TryNextMethod();
end;

InstallMethod(FindBasePointCandidates,
  "for reducible matrix group over a FF, use submodules and quotients",
  [ IsGroup and IsMatrixGroup and IsFinite, IsRecord, IsInt, IsObject ], 21,
  MSSFBPC);

GetInformationFromRecog:=function(recog)
local treerecurse,n,factors,homs,leafgens,niceranges,genum,sz,leafs,g,
      minranges,mingens,permap,furtherhom,fn,extragens,extranum,extra,allgens,
      nicegp,map,stc,orbit,gd,cnt;

  # the main worker function -- traverse the tree.
  treerecurse:=function(r,h)
  local
  hom,f,k,sel,u,i,j,x,cs,nsol,xf,bigger,nicehom,ngi,stbc,opt,act,ng,dom,v;
    if IsLeaf(r) then
      f:=Length(NiceGens(r));
      Info(InfoFFMat,2,"Leaf",Size(r)," ",genum," ",f);
      k:=[genum+1..genum+f];
      if Size(r)=1 then
 Info(InfoFFMat,2,"ignoring trivial factor");
      elif Length(Set(Factors(Size(r))))<3 then
 Info(InfoFFMat,2,"ignoring solvable factor of order ",Size(r));

 # store info
 n:=n+Length(Factors(Size(r)));
 SetIsSolvableGroup(Grp(r),true);
 permap[n]:=IdentityMapping(Grp(r));
 leafs[n]:=r;
 homs[n]:=h;
 factors[n]:=false;
 sz[n]:=Size(r);
 leafgens[n]:=f;

 sel:=[1..Length(NiceGens(r))];
 mingens[n]:=sel;
 niceranges[n]:=k;
 minranges[n]:=k{sel};

      else
 nicehom:=false;
 if IsPermGroup(Grp(r)) then
   nicehom:=IdentityMapping(Grp(r));
 elif IsBound(r!.stabilizerchain) then
   #use ActionOnOrbit?
   stbc:=BaseStabilizerChain(r!.stabilizerchain);
   act:=stbc.ops[1];
   if ForAll(stbc.ops,x->x=act) then
     # all the same action -- union
     orbit:=[];
     for j in [1..Length(stbc.points)] do
       if not stbc.points[j] in orbit then
  orbit:=Union(orbit,Orbit(Grp(r),stbc.points[j],stbc.ops[j]));
       fi;
     od;
     if Length(orbit)>1 and Length(orbit)<50000 then
       nicehom:=ActionHomomorphism(Grp(r),orbit,stbc.ops[1],"surjective");
       Info(InfoFFMat,2,"Got degree ",Length(orbit));
     fi;
   fi;
 fi;


 if nicehom=false then
# Hasfhmethsel, success method: StabilizerChain

   if IsBound(r!.projective) and r!.projective then
     g:=Grp(r);
     v:=OnLines(g.1[1],One(g));
     dom:=ShallowCopy(Orbit(g,v,OnLines));
     nicehom:=ActionHomomorphism(g,dom,OnLines,"surjective");
     while Size(Image(nicehom))<Size(r) do
       cnt:=0;
       repeat
  v:=OnLines(Random(DefaultFieldOfMatrixGroup(g)^Length(One(g))),
             One(g));
                cnt:=cnt+1;
  if cnt>1000 then Error("no vector found");fi;
       until not v in dom;
       Append(dom,Orbit(g,v,OnLines));
       nicehom:=ActionHomomorphism(g,dom,OnLines,"surjective");
     od;
   else
     nicehom:=IsomorphismPermGroup(Grp(r));
   fi;
 fi;
 g:=Image(nicehom);
 if Size(g)<>Size(r) then
   Error("some discrepancy happened");
 fi;

 #test!!
 # .isknownsimple, .isknownalmostsimple
 if IsBound(r!.comment) and r!.comment[1]<>'_' then
   SetIsSimpleGroup(g,true);
   gd:=g;
 elif not IsSolvableGroup(g) then
   gd:=PerfectResiduum(g);
 else
   gd:=fail;
 fi;

 if gd<>fail then
   if NrMovedPoints(g)> SufficientlySmallDegreeSimpleGroupOrder(Size(gd))
     then
       nicehom:=nicehom*SmallerDegreePermutationRepresentation(g);
       gd:=Image(nicehom);
       Info(InfoFFMat,2,"Improved degree ",NrMovedPoints(g),"->",
         NrMovedPoints(gd));
       if HasIsSimpleGroup(g) and IsSimpleGroup(g) then
  SetIsSimpleGroup(gd,true);
  SetIsSolvableGroup(gd,false);
       fi;
       g:=gd;
   fi;
 fi;

 ## indicate unsolvable
 #nsol:=IsSimpleGroup(g) or
 #  (Length(Set(Factors(Size(r))))>2 and not IsSolvableGroup(g));


 if IsSolvableGroup(g) then
   Info(InfoFFMat,2,"Ignoring solvable factor of order ",Size(g));
     n:=n+Length(Factors(Size(g)));
   elif not IsSimpleGroup(g) then
  Info(InfoFFMat,2,"doing size",Size(g)," ",IsSimpleGroup(g));

   # not simple -- split
   cs:=CompositionSeries(g);
   ng:=List(NiceGens(r),x->ImagesRepresentative(nicehom,x));
   for i in [2..Length(cs)] do
     extra:=[];
     n:=n+1;
     # test generators
     u:=cs[i];
     sel:=[];
     for j in [1..f] do
       x:=ng[j];
       if x in cs[i-1] and not x in u then
  Add(sel,j);
  u:=ClosureSubgroup(u,x);
       fi;
     od;

     nicegp:=Group(ng);
     map:=EpimorphismFromFreeGroup(nicegp);

     if Size(u)<Size(cs[i-1]) then
       Info(InfoFFMat,2,"cannot compatibilize generators -- add extras");
       while Size(u)<Size(cs[i-1]) do
         x:=First(GeneratorsOfGroup(cs[i-1]),x->not x in u);
  u:=ClosureSubgroup(u,x);

  # decompose into word
  xf:=Factorization(nicegp,x);
  if xf=fail then Error("factorization error");fi;
  x:=MappedWord(xf,MappingGeneratorsImages(map)[1],allgens{k});

  extragens[extranum]:=x;
  Add(extra,extranum);
  extranum:=extranum+1;
       od;
     fi;
     hom:=NaturalHomomorphismByNormalSubgroup(cs[i-1],cs[i]);
     if not IsAbelian(Image(hom)) then
       permap[n]:=IsomorphismPermGroup(Image(hom));
     fi;

     fn:=fn+1;
     if Size(cs[i])=1 then
       furtherhom[fn]:=nicehom;
     else
       furtherhom[fn]:=nicehom*hom;
     fi;
     leafs[n]:=false;
     homs[n]:=Concatenation(h,[fn]);
     factors[n]:=Image(hom);
     sz[n]:=Size(Image(hom));
     leafgens[n]:=false;
     niceranges[n]:=false;
     minranges[n]:=Concatenation(k{sel},extra);
   od;

 else
   # found a simple case
   n:=n+1;

   permap[n]:=nicehom;
   # get smaller generating set
   sel:=[];
   u:=TrivialSubgroup(Image(nicehom));
   for i in [1..Length(NiceGens(r))] do
     ngi:=ImagesRepresentative(nicehom,NiceGens(r)[i]);
     if not ngi in u then
       u:=ClosureGroup(u,ngi);
       Add(sel,i);
     fi;
   od;

   leafs[n]:=r;
   homs[n]:=h;
   factors[n]:=g;
   sz[n]:=Size(r);
   leafgens[n]:=f;

   mingens[n]:=sel;
   niceranges[n]:=k;
   minranges[n]:=k{sel};

 fi;

      fi;
      genum:=genum+f;
    else
      #hom:=Homom(r);
      f:=RIFac(r);
      treerecurse(f,Concatenation(h,[1]));
      k:=RIKer(r);
      if k<>fail then
 treerecurse(k,Concatenation(h,[0]));
      fi;
    fi;

  end;

  extragens:=[];
  furtherhom:=[];
  fn:=2;
  n:=0;
  genum:=0;
  factors:=[];
  homs:=[];
  leafgens:=[];
  leafs:=[];
  niceranges:=[];
  minranges:=[];
  mingens:=[];
  sz:=[];
  permap:=[];
  allgens:=NiceGens(recog);

  extranum:=Length(allgens)+1;
  treerecurse(recog,[]);

  return rec(
    group:=Grp(recog),
    n:=n,
    genum:=Length(allgens),
    recog:=recog,
    leafs:=leafs,
    factors:=factors,
    furtherhom:=furtherhom,
    sz:=sz,
    homs:=homs,
    leafgens:=leafgens,
    minranges:=minranges,
    mingens:=mingens,
    extragens:=extragens,
    permap:=permap,
    niceranges:=niceranges);
end;

# assume that hom i can be applied
CSIImageHomNr:=function(csi,n,x)
local r,h,i;
  r:=csi.recog;
  h:=csi.homs[n];
  for i in h do
    if i=0 then
      r:=RIKer(r);
    elif i=1 then
      x:=ImageElm(Homom(r),x);
      r:=RIFac(r);
    else
      x:=ImageElm(csi.furtherhom[i],x);
    fi;
  od;
  return x;
end;

# since nice ranges can contain negatives, we cannot simply sublist
CSINiceGens:=function(csi,a)
  if IsList(a) then
    return List(a,x->CSINiceGens(csi,x));
  elif a<=csi.genum then
    return NiceGens(csi.recog)[a];
  else
    return csi.extragens[a];
  fi;
end;

CSIDepthElm:=function(csi,x)
local i;
  i:=1;
  while i<=csi.n and
   (not IsBound(csi.leafs[i]) or # early skipped multiple solvable factor
   (
   (IsBool(csi.leafs[i]) or not (IsBound(csi.leafs[i]!.projective) and csi.leafs[i]!.projective)
     or csi.leafs[i]!.projective=false)
   and Order(CSIImageHomNr(csi,i,x))=1)
     or
    (not IsBool(csi.leafs[i]) and (IsBound(csi.leafs[i]!.projective) and csi.leafs[i]!.projective) and
    Order(ImagesRepresentative(csi.permap[i],CSIImageHomNr(csi,i,x)))=1)) do
    i:=i+1;
  od;
  return i;
end;

# to test whether an element act trivially projectively, it is not enough to
# test on base, but also need to have sum of base
# elm must be an element that is already image
CSIProjectiveBases:=function(csi,i,elm)
local m;
  if not IsBound(csi.projbases) then
    csi.projbases:=[];
  fi;
  if not IsBound(csi.projbases[i]) then
    m:=ShallowCopy(One(elm));
    Add(m,Sum(m));
    csi.projbases[i]:=m;
  fi;
  return csi.projbases[i];
end;

CSIDepthElm:=function(csi,x)
local i,ximg;
  i:=1;
  while i<=csi.n do
    if not IsBound(csi.leafs[i]) or
      ((IsBool(csi.leafs[i]) or not (IsBound(csi.leafs[i]!.projective)  and csi.leafs[i]!.projective)
 or csi.leafs[i]!.projective=false)
 and Order(CSIImageHomNr(csi,i,x))=1) then
      i:=i+1;
    elif (not IsBool(csi.leafs[i]) and IsBound(csi.leafs[i]!.projective) and
 csi.leafs[i]!.projective) then
      ximg:=CSIImageHomNr(csi,i,x);
      if ForAll(CSIProjectiveBases(csi,i,ximg),z->OnLines(z,ximg)=z) then
 i:=i+1;
      else
 return i;
      fi;
    else
      return i;
    fi;
  od;
  return i;
end;

CSIAelement:=function(a,localgens,l)
local limg,rep;
  limg:=List(l,x->Image(a[2],x));
  rep:= RepresentativeAction(a[1],localgens,limg,OnTuples);
  if rep=fail then
    Error("no rep");
  fi;
  return rep;
end;

FindAct:=function(csi)
local csinice,dci,pool,
act,n,i,j,k,l,gens,lgens,c,d,genims,gp,hom,auts,isoms,pools,poolnum,a,x,
isom,diso,conj,dgp,imgdepth,perms,kn,process,ii,goodgens,conjgens,genimgs,
doesaut,biggens,wrimages,m,w,e,poolimggens,img,localgens,dfgens,wrs,dfimgs,b,perm,lims,map,aels,wrsoc,dfs;


  # get a list of generator images and construct the appropriate element of
  # a[1] inducing these generator images by conjugation
  aels:=[];

  # dci is decomposition information needed for restriction to proper
  # subgroups
  dci:=rec(csi:=csi);


  # isoms[i] An isomorphism from the first group in its pool to group i

  goodgens:=[];
  poolimggens:=[];
  genimgs:=List([1..Maximum(Union(csi.minranges))],x->[]);
  n:=csi.n;
  csinice:=NiceGens(csi.recog);
  act:=[];
  auts:=[];
  pools:=[];
  perms:=[];
  isoms:=[];
  n:=csi.n;

  doesaut:=[];

  process:=[n];
  ii:=1;
  while ii<=n do
    # do we need to feed in another layer?
    if Length(process)<ii then
      Add(process,First([n,n-1..1],x->not x in process));
    fi;
    i:=process[ii];

    if IsBound(csi.permap[i]) and not IsSolvableGroup(Image(csi.permap[i])) then

      if not IsBound(goodgens[i]) then
 # no generators set yet -- just take new ones
 #lgens:=csinice{csi.minranges[i]};
 lgens:=CSINiceGens(csi,csi.minranges[i]);
 goodgens[i]:=lgens;
      else
        lgens:=goodgens[i];
      fi;

      gp:=Image(csi.permap[i]);
      act[i]:=[];
      gens:=List(lgens,x->ImagesRepresentative(csi.permap[i],CSIImageHomNr(csi,i,x)));

#      for x in [1..Length(gens)] do
# j:=CSIImageHomNr(csi,i,csinice[csi.minranges[i][x]]);
# k:=Image(csi.permap[i],j);
# if k<>gens[x] then
#   Error("GENS");
# fi;
#      od;

      # nonsolvable factor -- Is it in pools?
      poolnum:=First([1..Length(pools)],x->i in pools[x]);
      # pools will always be joined TO the current one, so isom will not
      # have to change on the way.
      if poolnum=fail then
 Add(pools,[i]);
 poolnum:=Length(pools);
 auts[poolnum]:=[];
 isoms[i]:=false; # representative
 isom:=IdentityMapping(gp);
 Add(poolimggens,gens);
      elif i<>pools[poolnum][1] then
 isom:=isoms[i]; # iso
      else
 isom:=IdentityMapping(gp); # first one -- iso is trivial
      fi;

      #find out what the previous factors do on it
      for j in Filtered([1..i-1],x->IsBound(csi.minranges[x])) do
 Info(InfoFFMat,2,"Try ",i," mapped by ",j);

 for kn in csi.minranges[j] do
   #k:=csinice[kn];
   k:=CSINiceGens(csi,kn);
   if not IsBound(perms[kn]) then
     perms[kn]:=[];
     genimgs[kn]:=[];
     doesaut[kn]:=[];
   fi;

   conjgens:=[];
   genims:=[];
   imgdepth:=fail;
   # calculate images
   for l in lgens do
     c:=l^k; # conjugate
     Add(conjgens,c);
     d:=CSIDepthElm(csi,c);
     if imgdepth=fail then
       # new image -- isomorphism
       imgdepth:=d;
       perms[kn][i]:=d; # record permutations
     elif imgdepth<>d then
       # this cannot happen
       Error("incompatible depths");
     fi;
     Add(genims,ImagesRepresentative(csi.permap[d],CSIImageHomNr(csi,d,c)));
   od;

   dgp:=Image(csi.permap[d]);
   if AssertionLevel()>2 then
     hom:=GroupHomomorphismByImages(gp,dgp,gens,genims);
   else
     hom:=GroupHomomorphismByImagesNC(gp,dgp,gens,genims);
   fi;
   if hom=fail then Error("should not happen");fi;

   if d=i then
     # pull generator images back in original group
     genimgs[kn][i]:=List(genims,x->PreImagesRepresentativeNC(isom,x));

#if ForAny(Flat(genimgs),x->not x in Image(csi.permap[pools[poolnum][1]])) then
#  Error("imerrD");
#fi;

     # component is not moved we just need to conjugate with isom
     hom:=isom*hom*InverseGeneralMapping(isom);
   SetIsBijective(hom,true);
     if not IsInnerAutomorphism(hom) then
       Add(auts[poolnum],hom);
       AddSet(doesaut[kn],i);
     elif not IsOne(hom) then
       # not automorphism, but still acting
       AddSet(doesaut[kn],i);
     fi;
   elif d in pools[poolnum] then
     # we know already that the groups are isomorphic -- just store
     # the new automorphism

     # isomorphism is always to the first element in the pool
     if d=pools[poolnum][1] then
       # the group is the normal form -- we do not need to translate
       diso:=IdentityMapping(dgp);
     else
       # isomorphism to canonical form
       diso:=InverseGeneralMapping(isoms[d]);
     fi;
     genimgs[kn][i]:=List(genims,x->ImagesRepresentative(diso,x));

# if ForAny(Flat(genimgs),x->not x in Image(csi.permap[pools[poolnum][1]])) then
#   Error("imerrC");
# fi;
     hom:=isom*hom*diso;
   SetIsBijective(hom,true);
     if not IsInnerAutomorphism(hom) then
       Add(auts[poolnum],hom);
       AddSet(doesaut[kn],i);
     fi;
   else
     # isomorphism wasn't known yet -- we need to join pools
     # hom is the joiner, isom*hom*isoms[d]^-1 the conjugator of the
     # canonical map

     # the pool we add. We always join the image pool to the current
     # one.
     a:=First([1..Length(pools)],x->d in pools[x]);
     if a=fail then
       # we did not yet know layer d -- add it
              Info(InfoFFMat,2,"Found Layer ",d);
       Add(pools[poolnum],d);
       isoms[d]:=isom*hom;
              Assert(3,IsBijective(isoms[d]));
       # newly included component -- the generators *are* simply the
       # images
       genimgs[kn][i]:=List(genims,
         x->PreImagesRepresentativeNC(isoms[d],x));

#if ForAny(Flat(genimgs),x->not x in Image(csi.permap[pools[poolnum][1]])) then
  #Error("imerrB");
#fi;
       goodgens[d]:=conjgens;
       Add(process,d);
     else
       Error("This cannot happen??");
       # we knew the layer and join pools
       Info(InfoFFMat,2,"Join ",pools[a]," to ",pools[poolnum]);
       conj:=isom*hom;
       if not d=pools[a][1] then
  # translate to normal form
  conj:=conj*InverseGeneralMapping(isoms[d]);
       fi;

       # join pools
       for x in pools[a] do
  Add(pools[poolnum],x);
  isoms[x]:=conj*isoms[x]; # reroot isomorphism
       od;

       # translate automorphisms
       for x in auts[a] do
  Add(auts[poolnum],conj*x*InverseGeneralMapping(conj));
       od;

       # delete old
       pools[a]:=[];
       auts[a]:=[];
     fi;
   fi;
 od;
      od;
    fi;
    ii:=ii+1;
  od;

  wrs:=[];
  wrsoc:=[];
  dfgens:=[];
  dfimgs:=[];

#if ForAny(Flat(genimgs),x->not x in Image(csi.permap[pools[poolnum][1]])) then
#  Error("imerrA");
#fi;

  if Length(pools)=0 then
    # solvable
    d:=Group(());
    a:=GeneratorsOfGroup(csi.group);
    b:=List(a,x->One(d));
    RUN_IN_GGMBI:=true; # hack to skip Nice treatment
    hom:=GroupHomomorphismByImagesNC(csi.group,d,a,b);
    RUN_IN_GGMBI:=false;
    dci:=rec(isTrivial:=true);
    SetRecogDecompinfoHomomorphism(hom,dci);
    SetImagesSource(hom,Group(()));
    return hom;
  fi;

  dci.pools:=pools;
  dci.wreathemb:=[];
  dci.goodgens:=goodgens;
  dci.isoms:=isoms;
  dci.poollocalgens:=[];

  # now build each group
  for i in [1..Length(pools)] do
    pool:=pools[i];
    m:=Length(pools[i]);
    a:=Image(csi.permap[pools[i][1]]);
    a:=[a,Concatenation(auts[i],
     List(GeneratorsOfGroup(a),x->InnerAutomorphism(a,x)))];
    a:=AutomorphismRepresentingGroup(a[1],a[2]);
    aels[i]:=a;
    localgens:=List(poolimggens[i],x->Image(a[2],x));
    dci.poollocalgens[i]:=localgens;
    b:=SymmetricGroup(m);
    w:=WreathProduct(a[1],b);
    e:=List([1..m+1],x->Embedding(w,x));
    dci.wreathemb[i]:=e;
    biggens:=[];
    wrimages:=[];

    for j in Filtered([1..n],x->IsBound(csi.minranges[x])) do
      c:=csi.minranges[j];
    Info(InfoFFMat,2,"Images for ",j," ",c);

      # does any of the elements actually do something -- if so, what?

#      if true or ForAny(c,x->IsBound(doesaut[x])
#        and ForAny(pools[i],y->y in doesaut[x])) then
        Info(InfoFFMat,2,c," acts ",pools[i],j);
 for kn in c do
   #Add(biggens,csinice[kn]);
   Add(biggens,CSINiceGens(csi,kn));
   img:=One(w);
   for l in [1..Length(pools[i])] do
            if IsBound(genimgs[kn][pools[i][l]]) then
       d:=Image(e[l],CSIAelement(a,localgens,genimgs[kn][pools[i][l]]));
     else
       if pools[i][l]=j then
  d:=List(goodgens[pools[i][l]],
   x->ImagesRepresentative(csi.permap[pools[i][l]],
     CSIImageHomNr(csi,pools[i][l],x^CSINiceGens(csi,kn))));
  if isoms[j]<>false then
    d:=List(d,x->PreImagesRepresentativeNC(isoms[j],x));
  fi;
  d:=Image(e[l],CSIAelement(a,localgens,d));
       else
  # component is not acted on as it lies higher
  d:=One(a[1]);
       fi;
     fi;
     img:=img*d;
   od;
   # is it a nontrivial permutation
   if IsBound(perms[kn]) and ForAny([1..Length(perms[kn])],
     x->IsBound(perms[kn][x]) and perms[kn][x]<>x) then
     # fill up fixed points
     for d in pool do
       if not IsBound(perms[kn][d]) then perms[kn][d]:=d;fi;
     od;

     # permuting part
     d:=PermList(List(perms[kn]{pool},x->Position(pool,x)));
     img:=img*Image(e[Length(pool)+1],d);
   fi;

   Add(wrimages,img);
 od;

    od;
    Add(wrs,w);
    a:=List([1..m],x->PerfectResiduum(Image(Embedding(w,x))));
    b:=TrivialSubgroup(w);
    for l in a do
      b:=ClosureGroup(b,l);
    od;
    SetDirectFactorsFittingFreeSocle(w,a);
    Add(wrsoc,b); # socle
    Add(dfgens,biggens);
    Add(dfimgs,wrimages);
  od;

  d:=DirectProduct(wrs);
  dci.dirprod:=d;
  dci.embeddings:=List([1..Length(pools)],x->Embedding(d,x));
  dci.aels:=aels;
  a:=List([1..Length(pools)],x->List(DirectFactorsFittingFreeSocle(wrs[x]),
    y->Image(dci.embeddings[x],y)));
  dfs:=Concatenation(a);
  SetDirectFactorsFittingFreeSocle(d,dfs);
  a:=dfgens[1];
  b:=List(a,x->One(d));
  w:=TrivialSubgroup(d);
  for i in [1..Length(pools)] do
    e:=dci.embeddings[i];
    w:=ClosureGroup(w,Image(e,GeneratorsOfGroup(wrsoc[i])));
    for j in [1..Length(a)] do
      Assert(1,dfgens[i][j]=a[j]);

      b[j]:=b[j]*Image(e,dfimgs[i][j]);
    od;
  od;

  if IsPermGroup(csi.group) and AssertionLevel()>2 then
    hom:=GroupHomomorphismByImages(csi.group,d,a,b);
  else
    RUN_IN_GGMBI:=true; # hack to skip Nice treatment
    hom:=GroupHomomorphismByImagesNC(csi.group,d,a,b);
    RUN_IN_GGMBI:=false;
  fi;
  if hom=fail then
    Error("fail!");
  fi;
  b:=Subgroup(d,b);
  #SetSocle(b,w);
  dci.socle:=w;
  dfs:=Filtered(dfs,x->IsSubset(b,x));
  SetDirectFactorsFittingFreeSocle(w,dfs);
  SetDirectFactorsFittingFreeSocle(b,dfs);
  SetImagesSource(hom,b);
  SetRecogDecompinfoHomomorphism(hom,dci);
  return hom;

end;


CSIElementAct:=function(dci,elm)
local csi,pools,result,i,e,a,img,b,dp,d,kn,perm,l;
  csi:=dci.csi;
  pools:=dci.pools;
  result:=One(dci.dirprod);
  for i in [1..Length(pools)] do
    e:=dci.wreathemb[i];
    a:=dci.aels[i];
    img:=One(Image(e[1]));
    perm:=[];
    for l in [1..Length(pools[i])] do
      b:=List(dci.goodgens[pools[i][l]],x->x^elm);
      dp:=CSIDepthElm(csi,b[1]);
      Assert(2,ForAll(b,x->CSIDepthElm(csi,x)=dp));
      perm[l]:=Position(pools[i],dp);

      d:=List(dci.goodgens[pools[i][l]],
       x->ImagesRepresentative(csi.permap[dp],
  CSIImageHomNr(csi,dp,x^elm)));
      if dci.isoms[dp]<>false then
 d:=List(d,x->PreImagesRepresentativeNC(dci.isoms[dp],x));
      fi;
      d:=Image(e[l],CSIAelement(a,dci.poollocalgens[i],d));
      img:=img*d;
    od;

    # permuting part
    d:=PermList(perm);
    img:=img*Image(e[Length(pools[i])+1],d);
    result:=result*Image(dci.embeddings[i],img);

  od;
  return result;
end;

#############################################################################
##
#M  ImagesRepresentative(<hom>,<x>)
##
InstallMethod(ImagesRepresentative,"for recognition mappings",
        FamSourceEqFamElm,
  [ IsGroupGeneralMapping and
  HasRecogDecompinfoHomomorphism,IsMultiplicativeElementWithInverse], 0,
function(hom, x)
local d;
  d:=RecogDecompinfoHomomorphism(hom);
  if IsBound(d.isTrivial) then return ();fi;
  if IsBound(d.fct) then
    return d.fct(x);
  else
    return CSIElementAct(RecogDecompinfoHomomorphism(hom),x);
  fi;
end);

#############################################################################
##
#M  PreImagesSetNC(<hom>,<x>)
##
InstallMethod(PreImagesSetNC,"for recognition mappings", CollFamRangeEqFamElms,
  [ IsGroupGeneralMapping and
  HasRecogDecompinfoHomomorphism,IsGroup], 0,
function(hom, U)
local gens,pre;
  gens:=SmallGeneratingSet(U);
  pre:=List(gens,x->PreImagesRepresentativeNC(hom,x));
  U:=RecogDecompinfoHomomorphism(hom).LiftSetup;
  U:=SubgroupByFittingFreeData(Source(hom),pre,gens,U.pcgs);
  return U;
end);


#TODO: Detect Nonsolvable permuters (via perms) and leave out of pool

BasePointsActionsOrbitLengthsStabilizerChain:=function(c)
local l,o;
  l:=[];
  while c<>false do
    o:=c!.orb;
    Add(l,[o!.orbit[1],o!.op,Length(o!.orbit)]);
    c:=c!.stab;
  od;
  return l;
end;


FactorspaceActfun:=function(field,bas)
local heads;
  bas:=TriangulizedMat(bas);
  heads:=HeadsInfoOfSemiEchelonizedMat(bas,Length(bas[1]));
  return function(v,g)
    local c;
    v:=v*g;
    v:=SiftedVectorForGaussianRowSpace(field,bas, heads, v );
    c:=PositionNonZero(v);
    if c<=Length(v) then
      v:=Inverse(v[c])*v;
    fi;
    MakeImmutable(v);
    return v;
  end;
end;

INVTRANSPCACHE:=[];
AsInverseTranspose:=function(x,g)
local a,p;
  a:=fail;
  p:=0;
  while a=fail and p<Length(INVTRANSPCACHE) do
    p:=p+1;
    if IsIdenticalObj(INVTRANSPCACHE[p][1],g) then
      a:=INVTRANSPCACHE[p][2];
      if p>50 then
 p:=Concatenation([p],[1..p-1],[p+1..Length(INVTRANSPCACHE)]);
        INVTRANSPCACHE:=INVTRANSPCACHE{p};
      fi;
    fi;
  od;
  if a=fail then
    a:=TransposedMat(Inverse(g));
    if Length(INVTRANSPCACHE)>100 then
      # clean out old
      INVTRANSPCACHE:=Concatenation([[g,a]],INVTRANSPCACHE{[1..90]});
    else
      INVTRANSPCACHE:=Concatenation([[g,a]],INVTRANSPCACHE);
    fi;
  fi;
  return OnRight(x,a);
end;


ModuleStructureBase:=function(mats)
local orbtranslimit,f,total,gens,mo,bas,basn,dims,a,p,vec,orb,t,dict,use,fct,
    new,alt,g,pnt,limit,whole,siftchain,sub,b,trymultipleorbits;

  orbtranslimit:=function(vec,fct,limit)
  local dict,orb,t,pnt,g,img,p,coinc;
    dict:=NewDictionary(vec,true,f^Length(vec));
    orb:=[vec];
    AddDictionary(dict,vec,1);
    t:=[One(gens[1])];
    pnt:=1;
    coinc:=true;
    while pnt<=Length(orb) do
      for g in gens do
 img:=fct(orb[pnt],g);
 p:=LookupDictionary(dict,img);
 if p=fail then
   Add(orb,img);
   if Length(orb)>limit then
     return fail;
   fi;
   AddDictionary(dict,img,Length(orb));
   Add(t,t[pnt]*g);
 elif coinc then
   coinc:=false;
 fi;
      od;
      pnt:=pnt+1;
    od;
    return [dict,orb,t];
  end;

  trymultipleorbits:=function(seeds,fct,limit)
  local best,bestl,bestc,i,orb;
    best:=fail;
    bestl:=0;
    bestc:=0;
    for i in seeds do
      # form orbit with possible initial canonization
      orb:=orbtranslimit(fct(i,One(gens[1])),fct,limit);
      if orb<>fail and Length(orb[2])>1 then
        if Length(orb[2])>bestl then
   best:=orb;
   bestl:=Length(orb[2]);
   bestc:=1;
   if bestl>100 then
     return best;
   fi;
        else
   bestc:=bestc+1;
   # if we got 30 times the same length then use it.
   if bestc>30 then
     return best;
   fi;
 fi;
      elif bestl>0 then
 # we found at least one and others failed
        return best;
      fi;
    od;
    return best;
  end;

  siftchain:=function(use,x)
  local i,img,u;
    i:=1;
    for u in use do
      img:=u.fct(u.vec,x);
      img:=LookupDictionary(u.dict,img);
      if img=fail then
        return [fail,i,x];
      fi;
      x:=x/u.t[img];
      i:=i+1;
    od;
    return x;
  end;

  limit:=1000;

  whole:=Group(mats);
  f:=FieldOfMatrixList(mats);
  total:=1;
  gens:=mats;
  mo:=GModuleByMats(gens,f);
  bas:=MTX.BasesCompositionSeries(mo);
  dims:=List(bas,Length);
  if ForAll([2..Length(bas)],x->IsSubset(bas[x],bas[x-1])) then
    basn:=List([2..Length(bas)],x->Filtered(bas[x],y->not y in bas[x-1]));
    bas:=Concatenation(basn);
  else
    a:=ShallowCopy(bas[2]); # 1 is empty
    p:=3;
    while p<=Length(bas) do
      t:=Difference(bas[p],a);
      t:=Difference(t,bas[p-1]);
      repeat
        vec:=List([1..Length(bas[p])-Length(bas[p-1])],x->Random(t));
      until RankMat(Concatenation(a,vec))=Length(bas[p]);
      a:=Concatenation(a,vec);
      p:=p+1;
    od;
    bas:=a;
  fi;
  use:=[];

  while Length(gens)>0 do
    basn:=List(bas,x->OnLines(x,One(gens[1])));
    p:=PositionProperty(basn,x->ForAny(gens,y->OnLines(x,y)<>x));
    if p=fail then
      p:=PositionProperty(bas,x->ForAny(gens,y->x*y<>x));
      vec:=bas{[p..Minimum(p+10,Length(bas))]};
      fct:=OnRight;
    elif Size(f)^p<limit then
      p:=PositionProperty(dims,x->Size(f)^x>limit);
      if p=fail then
        p:=Length(dims);
      else
        p:=p-1;
      fi;
      orb:=OrbitsDomain(Group(gens),NormedRowVectors(VectorSpace(f,bas{[1..dims[p]]},Zero(bas[1]))),OnLines);
      orb:=Filtered(orb,x->Length(x)>1);
      Sort(orb,function(a,b) return Length(a)>Length(b);end);
      vec:=List(orb,x->x[1]);
      fct:=OnLines;
    else
      vec:=bas{[p..Minimum(p+10,Length(bas))]};
      fct:=OnLines;
    fi;

    # nontrivial orbit
    orb:=trymultipleorbits(vec,fct,limit);
    if orb=fail then
      b:=p;

      a:=Reversed(Filtered(dims,x->x<b));
      p:=fail;
      while p=fail and a[1]<>0 do
 sub:=a[1];
 fct:=FactorspaceActfun(f,bas{[1..sub]});
 basn:=List(bas,x->fct(x,One(gens[1])));
 p:=Filtered([a[1]+1..Minimum(a[1]+10,Length(bas))],
       x->ForAny(gens,y->fct(basn[x],y)<>basn[x]));
 if p=[] then
   p:=Filtered([a[1]+10..Length(bas)],
         x->ForAny(gens,y->fct(basn[x],y)<>basn[x]));
 fi;
 if p=[] then
   p:=fail;
 else
   # try to find one with orbit not just length p but also not too
   # large
   p:=Reversed(p);
   while Length(p)>1 and Size(f)^(p[1]-a[1])/(Size(f)-1)>limit do
     p:=p{[2..Length(p)]};
   od;
   p:=p[1];
 fi;
 a:=a{[2..Length(a)]};
      od;
      if p<>fail then
 orb:=trymultipleorbits(bas{[p..Minimum(p+10,Length(bas))]},fct,limit);
 if orb=fail then
   # try dual action
   fct:=AsInverseTranspose;
   p:=First([b..Length(bas)],x->ForAny(gens,y->fct(bas[x],y)<>bas[x]));
   if p<>fail then
     p:=[p];
     for orb in [1..5] do
       Add(p,p[1]+orb);
       Add(p,p[1]-orb);
     od;
     p:=Filtered(p,x->x>0 and x<Length(bas));
     orb:=trymultipleorbits(bas{p},fct,limit);
   fi;
   if orb=fail then
     Info(InfoFFMat,2,"even too long in dual");
     return rec(points:=[],ops:=[]);
   else
     Info(InfoFFMat,2,"dual ");
   fi;
 else
   Info(InfoFFMat,2,"factor space ");
 fi;
      else
 return rec(points:=[],ops:=[]);
 Error("p=fail -- should not happen");
      fi;

    fi;

    dict:=orb[1];
    t:=orb[3];
    orb:=orb[2];
    vec:=orb[1];
    Info(InfoFFMat,2,"got Length ",Length(orb),":",
      Collected(Factors(total*Length(orb))));

    Add(use,rec(vec:=vec,fct:=fct,orb:=orb,dict:=dict,t:=t,gens:=gens));

    #sift 20 random elements
    new:=[];
    p:=Maximum(Minimum(1000,Length(orb)*Length(gens)),10);
    alt:=0;
    a:=fail;
    while a=fail and Length(new)<20 and alt<p do
      a:=PseudoRandom(whole);
      a:=siftchain(use,a);
      alt:=alt+1;
      if a[1]<>fail then
 if fct(vec,a)<>vec then
   Error("not stab");
 fi;
        if not IsOne(a) and not a in new then
   Add(new,a);
   alt:=0;
 fi;
        a:=fail; # sifted through
      fi;
    od;

    if a<>fail then
      gens:=ShallowCopy(use[a[2]].gens);
      Add(gens,a[3]);
      use:=use{[1..a[2]-1]};
      total:=Product(List(use,x->Length(x.orb)));
      Info(InfoFFMat,2,"construction fail on level ",a[2]," of ",Length(use),
        " -- redo ",total);
    else
      total:=total*Length(orb);
      gens:=new;
    fi;

  od;
  return rec(points:=List(use,x->x.vec),ops:=List(use,x->x.fct),
             order:=total);
end;

MATGRP_AddGeneratorToStabilizerChain:="2bdefined";

MATGRP_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 MATGRP_StabilizerChainInner layer=",layer);
    next:=rec(point:=cand.points[1],op:=cand.ops[1]);
    #next := GENSS_NextBasePoint(gens,cand,opt,parentS);
    #cand := next.cand;   # This could have changed

    S := GENSS_CreateStabChainRecord(parentS,gens,size,
                                     next.point,next.op,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
            Error("ARGH!");
            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);

    # as we add normalizing, we are done
    return S;

    # Are we done?
    if size <> false and Length(S!.orb) = size then
        S!.proof := true;
        Info(InfoGenSS,4,"Leaving MATGRP_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 := MATGRP_StabilizerChainInner(stabgens,size/Length(S!.orb),
                                                   layer+1,cand,opt,S);
        else
            S!.stab := MATGRP_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;
            while i < opt.ImmediateVerificationElements do
                i := i + 1;
                x := GENSS_RandomElementFromAbove(S,layer);
                if MATGRP_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 MATGRP_StabilizerChainInner layer=",layer);
    return S;
  end;


MATGRP_AddGeneratorToStabilizerChain:=
  function( S, el,pt,op )
    # 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);
 SS:=StabilizerChain(Group(el),rec(Cand:=rec(ops:=[op],points:=[pt]),Reduced:=false,StrictlyUseCandidates:=true));

        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 stab. chain ",
              "in layer ", SS!.layer, " from ",Length(SS!.stronggens) );
        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 := MATGRP_StabilizerChainInner([r.rem],false,
                           SS!.layer+1,rec(points:=[pt],ops:=[op]), 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;


SolvableBSGS:=function(arg)
local CBase, normalizingGenerator,df,ops,firstmoved,i,
solvNC,S,pcgs,x,r,c,w,a,bound,U,xp,depths,oldsz,prime,relord,gens,acter,ogens,stabs,n,strongs,stronglevs,laynums,slvec,layerzero,p,laynum,layers,sel,vals,stronglayers,layervecs,slpval,slp,baspts,levp,blocksz,lstrongs,lstrongsinv,bl,opt,check,primes,shortlim,orb,orbs,j,sz,goodbase,CHAINTEST,orblens;

  goodbase:=[];
  CHAINTEST:=function(X,str)
    while X<>false do
      #if IsBound(X!.stronggens) and
      #  Length(X!.layergens)>Length(Factors(Size(X))) then
      #  Error("UGH");
      #fi;
      if not X!.orb!.orbit[1] in goodbase.points then
 Error("new point!");
      fi;
      #if Length(X!.orb!.gens)<>Length(Set(X!.orb!.gens)) then
      #  Error("duplicate!");
      #fi;
      #if IsBound(X!.opt) and IsBound(X!.opt.StrictlyUseCandidates) and
      #  X!.opt.StrictlyUseCandidates=false then Error("eh5!"); fi;

      if IsBound(X!.orb!.gens) and Length(X!.orb!.gens)<>Length(Factors(Size(X))) then
        Error("length!");
      fi;

      if IsBound(X!.orb!.gens) and Length(Difference(X!.orb!.gens,strongs))>0 then
        Error("XTRA!");
      fi;

      if IsBound(X!.orb!.gensi) and List(X!.orb!.gens,Inverse)<>X!.orb!.gensi then
 Error(str,"inverse!");
      fi;
      if IsBound(X!.orb!.gensi) and ForAny(X!.orb!.schreiergen,IsInt)  and
 Maximum(Filtered(X!.orb!.schreiergen,IsInt))>Length(X!.orb!.gensi)
 then
 Error("length!");
      fi;
      X:=X!.stab;
    od;
  end;

  gens:=arg[Minimum(2,Length(arg))];
  if IsGroup(gens) then
    a:=gens;
    gens:=GeneratorsOfGroup(gens);
  else
    a:=Group(gens);
  fi;
  if false and Length(arg)>2 and Length(gens)>5+Length(Factors(arg[3])) then
    gens:=List([1..5+Length(Factors(arg[3]))],x->PseudoRandom(a));
    gens:=Set(gens);
    a:=Group(gens);
    check:=true;
    #SetSize(a,sz);
  else
    check:=false;
  fi;
  if IsBound(arg[3]) then
    sz:=arg[3];
    #SetSize(a,sz);
  else
    sz:=fail;
  fi;
  SetIsSolvableGroup(a,true);

  if Length(arg)=1 then
    acter:=gens;
  else
    acter:=arg[1];
    if IsGroup(acter) then
      acter:=GeneratorsOfGroup(acter);
    fi;
  fi;

  shortlim:=Size(DefaultFieldOfMatrixGroup(a))*3000;

  df:=DefaultFieldOfMatrixGroup(a);
  if false then
    # try vectors from big group
    w:=BasisVectorsForMatrixAction(Group(acter));
    w:=ImmutableMatrix(df,w);
    if Size(df)=2 then
      ops:=List(w,x->OnRight);
    else
      ops:=List(w,x->OnLines);
    fi;
  fi;
  w:=ModuleStructureBase(gens);
  if IsBound(arg[3]) and IsBound(w.order) and w.order<arg[3] then
    for i in gens[1] do
      Add(w.points,i);
      Add(w.ops,OnRight);
    od;
  fi;

  opt:=rec(RandomStabGens:=10,
         Cand:=rec(points:=w.points,ops:=w.ops),
      #TODO XXX Find better strategy for limit iof field is larger
         VeryShortOrbLimit:=shortlim
        );
  FUNCSPACEHASH:=[]; # needed in base point selection code
  CBase:=StabilizerChain(a,opt);
  if sz<>fail and Size(CBase)<sz then
    Info(InfoWarning,1,"Wrong size -- redo with `doall' option");
    return fail;
  fi;

  primes:=Set(Factors(Size(CBase)));
  FUNCSPACEHASH:=[];
  w:=BasePointsActionsOrbitLengthsStabilizerChain(CBase);
  Info(InfoGenSS,2,"Suggested Base Points",List(w,x->Position(opt.Cand.points,x[1])),
                   " Lengths ",List(w,x->x[3]));

  goodbase:=BaseStabilizerChain(CBase);
  w:=1;
  opt:=1;

  # Dixon's (1968 - the solvable length of a solvable linear group) bounds
  if IsPerm(gens[1]) then
    a:=Length(MovedPoints(gens));
    bound := Int( LogInt( Maximum(1,a ^ 5), 3 ) / 2 );
  elif IsMatrixOrMatrixObj(gens[1]) then
    a:=NrRows(gens[1]); # dimension

    # we would need proper log
    #bound := Int( (8.55*Log(a+1,10)+0.36 );
    # since it does not exist, replace 8.55 by 9 and simplify rounded up.
    # this anyhow only matters for catching wrong input, so its harmless
    bound := Log((a+1)^9,10)+1;
  else
    # no bound known
    bound:=infinity;
  fi;

  normalizingGenerator:=function(g)
  local oldstrong,oldsz,phq,pbp,pba;
    oldsz:=Size(S);
    oldstrong:=ShallowCopy(StrongGenerators(S));
    # work around the ommission of prior redundant base points
    pba:=Filtered([1..Length(goodbase.points)],x->goodbase.ops[x](goodbase.points[x],g)<>goodbase.points[x]);
    pbp:=pba[1];

    #Print("\n");
    #View(S);
    #Print("\n use ",pba,"\n");

    MATGRP_AddGeneratorToStabilizerChain(S,g,goodbase.points[pbp],goodbase.ops[pbp]);

    while Size(S)=oldsz do
      Error("orderr BUG !!!\n");
      MATGRP_AddGeneratorToStabilizerChain(S,g);
    od;
    return Difference(StrongGenerators(S),oldstrong);
  end;

  solvNC:=function()
  local N,process,layergens,U,g,h,r,V,x,comm,pow,wp,sift;
    N:=StabilizerChain(Group(StrongGenerators(S)),rec(Base:=CBase,Size:=Size(S),Reduced:=false,StrictlyUseCandidates:=true)); # really should be copy

    # determine relative order
    pow:=2;
    wp:=w*w;
    while not SiftGroupElement(N,wp).isone do
      wp:=wp*w;
      pow:=pow+1;
    od;
    if not IsPrimeInt(pow) then
      prime:=Factors(pow)[1];
      w:=w^(pow/prime);
    else
      prime:=pow;
    fi;
    Info(InfoFFMat,2,"Relative order ",pow," prime ",prime);

    process:=[w];
    layergens:=[];
    U:=[];
    for g in process do
      sift:=SiftGroupElement(S,g);
      if not sift.isone then
        Info(InfoFFMat,2,"SS=",Size(S)," ",Length(U));

        for h in layergens do
   comm:=Comm(g,h);
  #Print(Order(comm),"\n");
   if not SiftGroupElement(N,comm).isone then
     # wrong element -- get new generator for layer below
     a:=false;
     w:=comm;
     S:=N; # don't we want to forget what we added?
     return false;
   fi;
 od;

 # the sifted element will be as good as generator, but allows for
 # cleaner decomposition.
 g:=sift.rem;

 V:=normalizingGenerator(g);
 #g:=V[1];
  if g in U then Error("duplicate");fi;
 U:=Concatenation([g],U);
 Add(layergens,g);
 for x in acter do
   Add(process,g^x);
 od;

      fi;
    od;
    a:=true;
    return U;

  end;

  S:=StabilizerChain(Group(One(gens[1])),rec(Base:=CBase,Reduced:=false,StrictlyUseCandidates:=true));

  pcgs:=[];
  depths:=[];
  relord:=[];
  xp:=1;
  while xp<=Length(gens) do
    Info(InfoFFMat,2,"Processing ",xp);
    x:=gens[xp];
    # feed through derived series if necessary
    while not SiftGroupElement(S,x).isone do
      c:=0;
      w:=x;
      a:=false;
      oldsz:=Size(S);
      while not a do
        c:=c+1;
  if c>bound then
    # not solvable
    Error("not solvable");
  fi;

 U:=solvNC(); # will change a,w

      od;
Info(InfoFFMat,2,"Found Layer ",Size(S)/oldsz);
#View(S);
Info(InfoFFMat,2,"n");
      if prime^Length(U)<>Size(S)/oldsz then Error("layerlen"); fi;
      pcgs:=Concatenation(U,pcgs);
      Add(depths,Length(pcgs));
      Append(relord,ListWithIdenticalEntries(Length(U),prime));

    od;
    xp:=xp+1;
  od;
  n:=Length(pcgs);
  depths:=Reversed(List(depths,x->n+1-x));
  relord:=Reversed(relord);
  Add(depths,n+1);

  w:=BasePointsActionsOrbitLengthsStabilizerChain(S);
  Info(InfoGenSS,2,"USED Base Points",List(w,x->Position(goodbase.points,x[1])),
                   " Lengths ",List(w,x->x[3]));


  # now build the chain once more with memory so we can decompose

  blocksz:=[];

  Info(InfoFFMat,2,"NOW",Size(S)," ",Length(pcgs));
  if Length(Factors(Size(S)))<>Length(pcgs) then Error("redundancies");fi;

  # now sift the generators through so that the strong generators *ARE* a pcgs
  gens:=Reversed(pcgs); # start with low level gens again
  pcgs:=[];
  acter:=[];
  #S:=StabilizerChain(Group(One(gens[1])),rec(Base:=CBase,Reduced:=false,StrictlyUseCandidates:=true));
  #S := GENSS_CreateStabChainRecord(false,
#        [],1,
#        goodbase.points[1],
#        goodbase.ops[1],goodbase,
#        rec(Base:=CBase,Reduced:=false,StrictlyUseCandidates:=true,InitialHashSize:=10));
  S:=false; # work around special treatment for trivial group.
  oldsz:=1;
  stabs:=[];
  xp:=1;
  strongs:=[];
  stronglevs:=[];
  while xp<=Length(gens) do


    if S<>false then
      #CHAINTEST(S,"E");
      oldsz:=Size(S);
      Info(InfoFFMat,2,"ProcessiNg ",xp," ",Size(S));


#if Length(StrongGenerators(S))>Length(Factors(Size(S))) then Error("more2\n");fi;
      x:=gens[xp];
      x:=SiftGroupElement(S,x).rem;
      #SiftGroupElement(S,x);

      if not IsOne(x) then
 normalizingGenerator(x);
      fi;
    else
      x:=gens[1];
      # OrbitsWithLog will add powers as strong generators to make the tree shallower.
      # This messes with the correspondence of strong generators and pcgs elements

      S:=StabilizerChain(Group(x),rec(Base:=CBase,Reduced:=false,StrictlyUseCandidates:=true,
        OrbitsWithLog := false,RandomStabGens:=1,Size:=Order(x)));

      # Remove those ~!@#$ extra generators (powers of x) that are added
      # to make the tree shallower, but cause problems here.
      strongs:=[x];

      a:=S;

      while a<>false do
        a!.stronggens:=ShallowCopy(strongs);
        a!.layergens:=[1];
        if Length(a!.orb!.gens)>0 then
          if a!.orb!.gens<>strongs then
            a!.orb:=Orb(ShallowCopy(strongs),a!.orb[1],a!.orb!.op,rec(schreier:=true));
            a!.orb!.gensi:=List(a!.orb!.gens,Inverse);
            repeat
              Enumerate(a!.orb);
            until IsClosed(a!.orb);
            a!.orb!.depth:=Size(a!.orb)-1;
            #if Length(a!.orb)=1 then
            #  a!.orb!.schreiergen:=ShallowCopy(strongs);
            #else
            #  a!.orb!.schreiergen:=[];
            #fi;
          fi;
        fi;
        a:=a!.stab;
      od;

      #CHAINTEST(S,"F2");

      strongs:=[];

    fi;

    if Size(S)=oldsz then Error("no change!");fi;

    Add(pcgs,x);
    a:=Set(Filtered(StrongGenerators(S),x->not x in strongs));
    a:=Difference(a,List([0..Order(x)],y->x^y));
    if Length(a)>0 then
      # redo
      Info(InfoFFMat,1,"nanu-redo");
      #Error("nanu");
      return SolvableBSGS(arg[1],arg[2],arg[3]);
    fi;
    Append(strongs,[x]);

    #CHAINTEST(S,"F");


    Append(stronglevs,ListWithIdenticalEntries(1,n+1-xp));
    #if n+1-xp in depths then
    #  Add(stabs,StructuralCopy(S));
    #fi;
    xp:=xp+1;
  od;

  pcgs:=Reversed(pcgs);

  CBase:=BaseStabilizerChain(S);
  w:=BasePointsActionsOrbitLengthsStabilizerChain(S);
  Info(InfoGenSS,2,"Used Base Points",List(w,x->Position(goodbase.points,x[1])),
                   " Lengths ",List(w,x->x[3]));

  baspts:=[];
  levp:=[];
  xp:=[1..Length(pcgs)];
  a:=S;
  for i in [1..Length(CBase.points)] do
    p:=List(xp,x->Position(a!.orb!.orbit,CBase.ops[i](CBase.points[i],pcgs[x])));
    sel:=Filtered([1..Length(xp)],x->p[x]<>1);
    baspts{xp{sel}}:=ListWithIdenticalEntries(Length(sel),i);
    levp[i]:=xp{sel};
    blocksz{xp{sel}}:=p{sel}-1;
    xp:=Difference(xp,xp{sel});
    a:=a!.stab;
  od;

  slpval:=function(w)
  local a,i;
    a:=w[2]*vals[w[1]];
    for i in [3,5..Length(w)-1] do
      a:=(a+w[i+1]*vals[w[i]]) mod p;
    od;
    return a;
  end;

  layerzero:=[];
  laynum:=Length(depths)-1;
  layers:=List([1..n],x->First([laynum,laynum-1..1],y->x>=depths[y]));
  stronglayers:=layers{stronglevs};
  # get vectors for strong generators (b/c we decompose into these)
  slvec:=[];
  for i in [1..laynum] do
    p:=relord[depths[i]];
    a:=IdentityMat(depths[i+1]-depths[i]);
    layerzero[i]:=Zero(a[1]);
    sel:=Positions(stronglayers,i);
    slvec{sel}:=a{List(strongs{sel},x->Position(pcgs,x))-depths[i]+1};

#    layervecs:=ListWithIdenticalEntries(n,fail); # this will trap use of
#    # higher gens
#    layervecs{[depths[i]..depths[i+1]-1]}:=a;
#
#    for j in [depths[i+1]..n] do
#      layervecs[j]:=Zero(a[1]);
#    od;
#    slp:=LinesOfStraightLineProgram(SLPOfElms(strongs{sel}));
#    vals:=Reversed(layervecs);
#    for j in slp do
#      if ForAll(j,IsList) then
# #last line
# slvec{sel}:=List(j,x->slpval(x));
#      elif IsList(j[1]) then
# Error("reassign syntax?");
#      else
# Add(vals,slpval(j));
#      fi;
#    od;
  od;
  ForgetMemory(strongs);
  ForgetMemory(gens);
  ForgetMemory(S);

  S!.pcgs:=pcgs;
  S!.layerzero:=layerzero;
  S!.layerprimes:=relord{depths{[1..laynum]}};
  S!.strongvecs:=slvec;
  S!.layranges:=List([1..laynum],x->[depths[x]..depths[x+1]-1]);
  S!.revranges:=List([1..laynum],x->Reversed([1..Length(S!.layranges[x])]));

  # reindexing
  a:=S;
  while a<>false do
    p:=List(a!.orb!.gensi,x->Position(strongs,x^-1));
    if fail in p then
      Error("not found!");
    fi;
    a!.gensistrongpos:=p;
    a!.gensilayers:=stronglayers{p};
    a:=a!.stab;
  od;

  #TODO: Use chain for decomposition -- at the moment it uses subgroups
  a:=pcgs;
  pcgs:=PcgsByPcSequenceCons(IsPcgsDefaultRep,
    IsPcgsMatGroupByStabChainRep and IsPcgs and IsPrimeOrdersPcgs,
    FamilyObj(gens[1]),pcgs,[]);
  pcgs!.stabilizerChain:=S;
  #pcgs!.stabs:=Reversed(stabs);
  pcgs!.laynums:=[1..laynum];
  pcgs!.layranges:=S!.layranges;
  pcgs!.baspts:=baspts;
  pcgs!.blocksz:=blocksz;
  pcgs!.levp:=levp;
  pcgs!.invpows:=
    List([1..Length(pcgs)],x->List([1..relord[x]-1],y->a[x]^(-y)));
  SetRelativeOrders(pcgs,relord);
  SetIndicesEANormalSteps(pcgs,depths);

  return rec(
    pcgs:=pcgs,
    depths:=depths,
    relord:=relord,
    baspts:=baspts,
    blocksz:=blocksz);
end;

# old ersion of exponent routines, allowing for arbitrary orbit arrangemnts
MatPcgsSift:=function( S, x,l )
local o,p,po,preS,r,v,vecs,prime;
  v:=S!.layerzero[l];
  prime:=S!.layerprimes[l];
  vecs:=S!.strongvecs;
  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
   Error("element not in group");
   return rec( v:=v,rem := x, S := S );
      fi;
      # Now sift through Schreier tree:
      while po > 1 do
 r:=o!.schreiergen[po];
 x := x * S!.orb!.gensi[r];
 po := o!.schreierpos[po];
 if S!.gensilayers[r]=l then
   # generator on the desired layer -- add up vectors
   v:=(v+vecs[S!.gensistrongpos[r]]) mod prime;
 fi;
      od;
      S := S!.stab;
  od;
  return v;
end ;

MatPcgsExponentsOld:=function(S,laynums,x)
local a,j,i,v;
  v:=[];
  for i in [1..Length(laynums)] do
    #S:=stabs[laynums[i]];
    a:=MatPcgsSift(S,x,laynums[i]);
    Append(v,a);
    if i<Length(laynums) then
      # need to divide off what is given by the vector
      for j in [1..Length(a)] do
        if a[j]<>0 then
   x:=LeftQuotient(S!.pcgs[S!.layranges[laynums[i]][j]]^a[j],x);
 fi;
      od;
    fi;
  od;
  return v;
end;


# new routine, using orbit positions
#decomposition as product, but not in canonical order, thus in multi stages
#TODO compare cost matrix multiplication vs. collection
MatPcgsExponents:=function(arg)
local pcgs,laynums,ox,o,p,po,preS,r,isone,ind,i,prd,S,q,rem,bs,pS,x,dep,e,layer,delta,
      z,prime,curran,seli,md,pos,sel,deponly;

  pcgs:=arg[1];
  laynums:=arg[2];
  ox:=arg[3];
  deponly:=Length(arg)>3 and arg[4];

  dep:=IndicesEANormalSteps(pcgs);
  md:=laynums[Length(laynums)]; #the layer depth which we ignore.

  z:=ListWithIdenticalEntries(dep[Length(dep)]-1,0);
  e:=ShallowCopy(z);
  pS:=pcgs!.stabilizerChain;

  repeat
    # factor the element using orbit positions  -- this is correct only for
    # the topmost layer used
    x:=ox;
    S:=pS;
    prd:=[];
    pos:=[];
    preS := false;
    ind:=1;
    layer:=md; # Maximal layer we do
    delta:=ShallowCopy(z); # we need to forget all in the previous layer
    curran:=pcgs!.layranges[layer];
    prime:=RelativeOrders(pcgs)[curran[1]];
    while S <> false do
      sel:=pcgs!.levp[ind];
      bs:=pcgs!.blocksz{sel};

      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)-1;

      # decompose
      for i in [1..Length(sel)] do
 seli:=sel[i];

 q:=QuoInt(po,bs[i]);
 rem:=po-q*bs[i];
 if q>0 then
   if seli<dep[layer] then
     # decrease layer in which we decompose
     layer:=layer-1;
     while seli<dep[layer] do
       layer:=layer-1;
     od;
     delta:=ShallowCopy(z); # we need to forget all in the previous layer
     prime:=RelativeOrders(pcgs)[seli];
     curran:=pcgs!.layranges[layer];
   fi;
   #Add(prd,[sel[i],q]);;

   # remember exponent entry if right layer
   if seli<dep[layer+1] then
     delta[seli]:=delta[seli]+q mod prime;
   fi;

   #x:=x/(pcgs[sel[i]]^q);
   x:=x*pcgs!.invpows[seli][q]; # use stored inverse powers

 fi;
 po:=rem;
      od;

      #if o[1]<>o!.op(o[1],x) then Error("not all off!");fi;

      S := S!.stab;
      ind:=ind+1;
    od;

    if delta<>fail then
      # now the affected (topmost) layer is correct
      e:=e+delta;

      # we don't need to correct the last layer we use
      if deponly and not IsZero(delta) then
 delta:=fail; # pop out.
      elif layer<laynums[Length(laynums)] then
 # we have d describe a the highest level. So we want x=d*newx
 for i in curran do
   if delta[i]>0 then
     ox:=pcgs!.invpows[i][delta[i]]*ox;
   fi;
 od;
      fi;

    fi;

  # stop when we've set the lowest layer or if the remainder is the identity
  until layer=laynums[Length(laynums)] or delta=fail;

  # do we only want some?
  if laynums=pcgs!.laynums then
    return e;
  else
    return e{Concatenation(pcgs!.layranges{laynums})};
  fi;

end;

InstallMethod(ExponentsOfPcElement,"matrix pcgs",IsCollsElms,
  [IsPcgsMatGroupByStabChainRep and IsPcgs and IsPrimeOrdersPcgs,
   IsMatrixOrMatrixObj],
function(pcgs,x)
  return MatPcgsExponents(pcgs,pcgs!.laynums,x);
end);

InstallOtherMethod(ExponentsOfPcElement,"matrix pcgs,indices",IsCollsElmsX,
  [IsPcgsMatGroupByStabChainRep and IsPcgs and IsPrimeOrdersPcgs,
   IsMatrixOrMatrixObj,IsList],
function(pcgs,x,inds)
local i,j,sel,lay,ip,sl,r,use;
  # is it a layer?
  for i in pcgs!.laynums do
    if inds=pcgs!.layranges[i] then
      # only this layer
      return MatPcgsExponents(pcgs,[i],x);
    fi;
  od;
  # which layers do we need?
  if not IsSortedList(inds) then
    # perverse case -- old
    return MatPcgsExponents(pcgs,pcgs!.laynums,x){inds};
  fi;
  sel:=[];
  lay:=[];
  ip:=1;
  sl:=0;
  for i in pcgs!.laynums do
    r:=pcgs!.layranges[i];
    use:=false;
    for j in [1..Length(r)] do
      if ip<=Length(inds) and  r[j]=inds[ip] then
 Add(sel,j+sl);
 use:=true;
        ip:=ip+1;
      fi;
    od;
    if use then
      AddSet(lay,i);
      sl:=sl+Length(r);
    fi;
  od;
  return MatPcgsExponents(pcgs,lay,x){sel};
end);

InstallMethod(DepthOfPcElement,"matrix pcgs",IsCollsElms,
  [IsPcgsMatGroupByStabChainRep and IsPcgs and IsPrimeOrdersPcgs,
  IsMatrixOrMatrixObj],
function(pcgs,x)
local e;
  e:=MatPcgsExponents(pcgs,pcgs!.laynums,x,true);
  return PositionNonZero(e);
end);

BindGlobal("TFDepthLeadExp",function(pcgs,x)
local p;
  x:=MatPcgsExponents(pcgs,pcgs!.laynums,x,true); # first nonzero
  p:=PositionNonZero(x);
  if p>Length(x) then
    # identity
    return [p,0];
  else
    return [p,x[p]];
  fi;
end);

InstallMethod(FittingFreeLiftSetup,"fields, using recognition",true,
  [IsMatrixGroup],0,
function(G)
local csi,r,factorhom,sbs,k,pc,hom,rad,it,i,sz,x,stop;
  r:=DefaultFieldOfMatrixGroup(G);
  if not IsField(r) then
    TryNextMethod();
  fi;

  r:=RecognizeGroup(G);
  SetSize(G,Size(r));
  csi:=GetInformationFromRecog(r);
  G!.storedrecog:=csi;
  factorhom:=FindAct(csi);


  #TODO: Better kernel gens by random selection
  sz:=Size(G)/Size(Image(factorhom));
  if Size(Image(factorhom))=1 then
    # solvable
    k:=GeneratorsOfGroup(G);
  else
    it:=CoKernelGensIterator(InverseGeneralMapping(factorhom));
    k:=Filtered(List([1..csi.genum],x->CSINiceGens(csi,x)),x->IsOne(ImagesRepresentative(factorhom,x)));
    if sz>1 then
      stop:=true;
      repeat
 for i in [1..3*Length(Factors(sz))] do
   if not IsDoneIterator(it) then
     x:=NextIterator(it);
     if not IsOne(x) and not x in k then
       Add(k,x);
     fi;
   fi;
 od;
 if sz>1 and Length(k)<Length(Factors(sz)) then stop:=false; fi; # work around issue
 if ValueOption("doall")=true then stop:=false;fi;
      until stop or IsDoneIterator(it);
    fi;
  fi;

  Info(InfoGenSS,3,"|k|=",Length(k));
  # TODO: Proper test

  if ForAll(k,IsOne) then
    rad:=TrivialSubgroup(G);
    sbs:=rec(pcgs:=[],depths:=[1],relord:=[],pcisom:=IsomorphismPcGroup(rad),radical:=rad);
  else
    sbs:=SolvableBSGS(G,k,sz);
    while sbs=fail do
      sbs:=SolvableBSGS(G,k,sz:doall);
    od;
    rad:=SubgroupNC(G,sbs.pcgs);
    SetSize(rad,Product(RelativeOrders(sbs.pcgs)));
    sbs.radical:=rad;
    pc:=PcGroupWithPcgs(sbs.pcgs);
    RUN_IN_GGMBI:=true; # hack to skip Nice treatment
    hom:=GroupHomomorphismByImagesNC(rad,pc,sbs.pcgs,FamilyPcgs(pc));
    RUN_IN_GGMBI:=false;
    SetIsBijective(hom,true);
    sbs.pcisom:=hom;
  fi;
  sbs.csi:=csi;
  SetKernelOfMultiplicativeGeneralMapping(factorhom,rad);
  sbs.factorhom:=factorhom;
  RecogDecompinfoHomomorphism(factorhom)!.LiftSetup:=sbs;

  return sbs;
end);

FFStats:=function(g)
local start,f;
  start:=Runtime();
  f:=FittingFreeLiftSetup(g);
  Print("Time:",Runtime()-start,"\n");
  Print("Factordegree ",NrMovedPoints(Range(f.factorhom)),"\n");
  Print("PcgsDegrees ",Maximum(List(
    BasePointsActionsOrbitLengthsStabilizerChain(f.pcgs!.stabilizerChain),
    x->x[3])),"\n");

end;

# work over residue class rings

MyZmodnZObj:=function(a,b)
  if IsPrimeInt(b) and b<65536 then
    return Z(b)^0*a;
  else
    return ZmodnZObj(a,b);
  fi;
end;

ReduceModM:=function(a,m)
  local b,r,i,j;
  if IsList(a) then
    if IsList(a[1]) then
      # matrix
      b:=[];
      for i in a do
 r:=[];
 for j in i do
   if IsZmodnZObjNonprime(j) then
     Add(r,MyZmodnZObj(j![1],m));
   else
     Add(r,MyZmodnZObj(j,m));
   fi;
 od;
 Add(b,r);
      od;
      if IsPrimeInt(m) then
 b:=ImmutableMatrix(m,b);
      fi;
      return b;
    else
      # vector
      b:=[];
      for j in a do
--> --------------------

--> maximum size reached

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

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