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


 stbc.gi   Interaktion und
Portierbarkeitunbekannt

 
#############################################################################
##
##  This file is part of GAP, a system for computational discrete algebra.
##  This file's authors include Heiko Theißen, Ákos Seress.
##
##  Copyright of GAP belongs to its developers, whose names are too numerous
##  to list here. Please refer to the COPYRIGHT file for details.
##
##  SPDX-License-Identifier: GPL-2.0-or-later
##

#############################################################################
##
#F  StabChain( <G>, <options> ) . . . . . . . . . . . . make stabilizer chain
##
InstallGlobalFunction( StabChain, function( arg )
    if Length( arg ) = 1  then
        return StabChainImmutable( arg[ 1 ] );
    else
        return Immutable( StabChainOp( arg[ 1 ], arg[ 2 ] ) );
    fi;
end );

InstallMethod( StabChainImmutable,"use StabChainMutable",
  true, [ IsObject ], 0, StabChainMutable );

InstallMethod( StabChainMutable,"call StabChainOp", true, [ IsGroup ], 0,
    G -> StabChainOp( G, rec(  ) ) );

InstallOtherMethod( StabChainOp,"with base", true, [ IsPermGroup,
        IsList and IsCyclotomicCollection ], 0,
    function( G, base )
    return StabChainOp( G, rec( base := base ) );
end );

InstallOtherMethod( StabChainOp,"empty base", true,
  [ IsPermGroup, IsList and IsEmpty ], 0,
    function( G, base )
    return StabChainOp( G, rec( base := base ) );
end );

InstallMethod( StabChainOp,"trivial group",
  [ IsPermGroup and IsTrivial, IsRecord ],
    function( G, options )
    local   S,  T,  pnt;

    S := EmptyStabChain( [  ], One( G ) );
    if     IsBound( options.base )
       and (        IsBound( options.reduced )
                and not options.reduced
             or     not IsBound( options.reduced )
                and not DefaultStabChainOptions.reduced )  then
        T := S;
        for pnt  in options.base  do
            InsertTrivialStabilizer( T, pnt );
            T := T.stabilizer;
        od;
    fi;
    return S;
end );

InstallMethod( StabChainOp,"group and option",
  [ IsPermGroup, IsRecord ],
    function( G, options )
    local   S,  T,  degree,  pcgs;

    # If a stabilizer chain <S> is already known, modify it.
    if HasStabChainMutable( G )  then
        S := StructuralCopy( StabChainMutable( G ) );
        if IsBound( options.base )  then
            if not IsBound( options.reduced )  then
                options.reduced := DefaultStabChainOptions.reduced;
            fi;
            if not ChangeStabChain( S, options.base, options.reduced )
               then
                return false;
            fi;
        elif IsBound( options.reduced )  and  options.reduced  then
            ReduceStabChain( S );
        fi;

    # Otherwise construct a new GAP object <S>.
    else
        CopyOptionsDefaults( G, options );

        # For solvable groups, use the pcgs algorithm.
        pcgs := [  ];
        if     options.tryPcgs and (not IsBound(options.base))
           and (# the group is know to be solvable
             (HasIsSolvableGroup(G) and IsSolvableGroup(G))
                # or the degree is small and the group is not known to be
                # insolvable
                or (Length(MovedPoints(G))<=100 and not
                    (HasIsSolvableGroup(G) and not IsSolvableGroup(G))
                    )) then
            S := EmptyStabChain( [  ], One( G ) );
            if IsBound( options.base )  then  S.base := options.base;
                                        else  S.base := [  ];          fi;
            if HasPcgs(G) and IsBound(Pcgs(G)!.stabChain) then
              # is there already a pcgs with a stabchain?
              # the translation to a record is  necessary to be able to copy
              # the stab chain.
              pcgs:=rec(stabChain:=CopyStabChain(Pcgs(G)!.stabChain));
            else
              pcgs := TryPcgsPermGroup( [ G, GroupStabChain( G, S, true ) ],
                              # get the series elementary abelian -- its much
                              # better
                            false, false, true );
            fi;
        fi;
        if IsPcgs( pcgs )  then
          options.random := 1000;
          S := pcgs!.stabChain;

          if not HasPcgs(G) then
            # remember the pcgs
            SetPcgs(G,pcgs);
            SetPcgsElementaryAbelianSeries(G,pcgs);
            S := CopyStabChain(S); # keep the pcgs' pristine stabchain
            if IsBound(options.base) then
              ChangeStabChain( S, options.base, options.reduced );
            fi;
          fi;
        elif IsRecord(pcgs) then
          S:=pcgs.stabChain;
          if IsBound(options.base) then
            ChangeStabChain( S, options.base, options.reduced );
          fi;
        else
            degree := LargestMovedPoint( G );
            if degree > 100  then

                # random Schreier-Sims
                S := StabChainRandomPermGroup(
                         ShallowCopy( GeneratorsOfGroup( G ) ), One( G ),
                         options );

            else

                # ordinary Schreier Sims
                S := EmptyStabChain( [  ], One( G ) );
                Unbind( S.generators );
                if not IsTrivial( G )  then
                    if not IsBound( options.base )  then
                        options.base := [  ];
                    fi;
                    S.cycles := [  ];
                    StabChainStrong( S, GeneratorsOfGroup( G ), options );
                    T := S;
                    while IsBound( T.stabilizer )  do
                        T.generators := T.labels{ T.genlabels };
                        Unbind( T.cycles );
                        T := T.stabilizer;
                    od;
                    T.generators := T.labels{ T.genlabels };
                    Unbind( T.cycles );
                else
                  S.generators:=[];
                fi;

            fi; # random / deterministic
        fi;

        # Now extend <S>, if desired.
        if not options.reduced  and  IsBound( options.base )  then
            ExtendStabChain( S, options.base );
        fi;

    fi;

    # if the parent is random, this group should be also
    # at base change or strong gens constr, may be no info about random
    if IsBound( options.random ) then
        if IsBound( StabChainOptions( Parent( G ) ).random )  then
            options.random := Minimum( StabChainOptions( Parent( G ) ).random,
                                      options.random );
        fi;
        StabChainOptions( G ).random := options.random;
    fi;

    SetStabChainMutable( G, S );
    return S;
end );

#############################################################################
##
#F  TrimStabChain( <C>,<n> )
##
##
InstallGlobalFunction(TrimStabChain,function( C,n )
local i;
  # typically all permutations in a stabilizer chain are just links to the
  # `labels' component. Thus reducing here will make them all small.
  for i in C.labels do
    if IsInternalRep(i) then
      TRIM_PERM(i,n);
    fi;
  od;
end);

#############################################################################
##
#F  CopyStabChain( <C> )  . . . . . . . . . . . . . . . . . . . copy function
##
##  This function produces a memory-disjoint copy of a stabilizer chain, with
##  `labels'  components   possibly   shared by  several  levels,   but  with
##  superfluous labels  removed. An entry  in  `labels' is superfluous  if it
##  does not occur among  the  `genlabels' or `translabels'   on any  of  the
##  levels which share that `labels' component.
##
##  This is useful for  stabiliser sub-chains that  have been obtained as the
##  (iterated) `stabilizer' component of a bigger chain.
##
InstallGlobalFunction(CopyStabChain,function( C1 )
    local   C,Xlabels,  S,  len,  xlab,  need,  poss,  i, copyChain;

    copyChain := function(C, labelorig, labelcpy)
        local obj, r, objNum, objPos, l;
        obj := rec();
        # Need to do labels first
        objNum := MASTER_POINTER_NUMBER(C.labels);
        objPos := Position(labelorig, objNum);
        if objPos <> fail then
            obj.labels := labelcpy[objPos];
        else
            Add(labelorig, objNum);
            l := List(C.labels);
            Add(labelcpy, l);
            obj.labels := l;
        fi;

        for r in RecNames(C) do
            if r = "stabilizer" then
                obj.(r) := copyChain(C.(r), labelorig, labelcpy);
            elif r = "labels" then
                ;# skip
            elif IsList(C.(r)) then
                obj.(r) := List(C.(r));
            else
                obj.(r) := C.(r);
            fi;
        od;
        return obj;
    end;

    # To begin with, make a deep copy.
    C := copyChain( C1, [], [] );

    # First pass: Collect the necessary genlabels.
    Xlabels := [  ];
    S := C;
    while IsBound( S.stabilizer )  do
        len := Length( S.labels );
        if len = 0  or  IsPerm( S.labels[ len ] )  then
            Add( S.labels, [ 1 ] );  len := len + 1;
            Add( Xlabels, S.labels );
        fi;
        UniteSet( S.labels[ len ], S.genlabels );
        UniteSet( S.labels[ len ], S.translabels );
        S := S.stabilizer;
    od;

    # Second pass: Find the new positions of the labels.
    for xlab  in Xlabels  do
        need := Last( xlab );

        # If all labels are needed, change nothing.
        if Length( need ) = Length( xlab ) - 1  then
            Remove( xlab );

        else
            poss := [  ];
            for i  in [ 1 .. Length( need ) ]  do
                poss[ need[ i ] ] := i;
            od;
            Add( xlab, poss );
        fi;
    od;

    # Third pass: Update the genlabels and translabels.
    S := C;
    while IsBound( S.stabilizer )  do
        len := Length( S.labels );
        if len <> 0  and  not IsPerm( S.labels[ len ] )  then
            poss := S.labels[ len ];
            S.genlabels := poss{ S.genlabels };
            S.translabels{ S.orbit } := poss{ S.translabels{ S.orbit } };
        fi;
        S := S.stabilizer;
    od;

    # Fourth pass: Update the labels.
    for xlab  in Xlabels  do
        len := Length( xlab );
        if len <> 0  and  not IsPerm( xlab[ len ] )  then
            need := xlab[ Length( xlab ) - 1 ];
            xlab{ [ 1 .. Length( need ) ] } := xlab{ need };
            for i  in [ Length( need ) + 1 .. Length( xlab ) ]  do
                Unbind( xlab[ i ] );
            od;
        fi;
    od;

    return C;
end);

#############################################################################
##
#M  StabChainOptions( <G> ) . . . . . . . . . . . . . for a permutation group
##
InstallMethod( StabChainOptions, true, [ IsPermGroup ], 0,
    G -> rec(  ) );

#############################################################################
##
#V  DefaultStabChainOptions . . . . . .  options record for stabilizer chains
##
BindGlobal( "DefaultStabChainOptions",rec( reduced := true,
                                 random := 1000,
                                tryPcgs := true ));
MakeThreadLocal("DefaultStabChainOptions");

#############################################################################
##
#F  CopyOptionsDefaults( <G>, <options> ) . . . . . . . copy options defaults
##
InstallGlobalFunction(CopyOptionsDefaults,function( G, options )
    local   P,  name;

    # See whether we know a base for <G>.
    if not IsBound( options.knownBase )  then
        if HasStabChainMutable(G) then
          options.knownBase := BaseStabChain(StabChainMutable(G));
        elif   HasBaseOfGroup( G )  then
          options.knownBase := BaseOfGroup( G );
        else
          P := Parent( G );
          while     not HasBaseOfGroup( P )
                and not IsIdenticalObj( P, Parent( P ) )  do
            P := Parent( P );
          od;
          if HasStabChainMutable(P) then
            options.knownBase := BaseStabChain(StabChainMutable(P));
          elif HasBaseOfGroup( P )  then
            options.knownBase := BaseOfGroup( P );
          fi;
        fi;
    fi;

    # See whether we know the exact size.
    if not IsBound( options.size )  then
        if HasSize( G )  then
            options.size := Size( G );
        elif IsBound( StabChainOptions( G ).size )  then
            options.size := StabChainOptions( G ).size;
        fi;
    fi;

    # Copy the default values.
    for name  in RecNames( DefaultStabChainOptions )  do
        if not IsBound( options.( name ) )  then
            if IsBound( StabChainOptions( G ).( name ) )  then
                options.( name ) := StabChainOptions( G ).( name );
            else
                options.( name ) := DefaultStabChainOptions.( name );
            fi;
        fi;
    od;

    # In the case of random construction, see whether  we know an upper limit
    # for the size.
    if IsBound( options.size ) then
        options.limit := options.size;
    elif not IsBound( options.limit )  then
        if IsBound( StabChainOptions( G ).limit )  then
            options.limit := StabChainOptions( G ).limit;
        else
            P := Parent( G );
            while     not HasSize( P )
                  and not IsBound( StabChainOptions( P ).limit )
                  and not IsIdenticalObj( P, Parent( P ) )  do
                P := Parent( P );
            od;
            if HasSize( P )  then
                options.limit := Size( P );
            elif IsBound( StabChainOptions( P ).limit )  then
                options.limit := StabChainOptions( P ).limit;
            fi;
        fi;
    fi;

end);

#############################################################################
##
#F  StabChainBaseStrongGenerators( <base>, <sgs>[, <one>] )
##
InstallGlobalFunction(StabChainBaseStrongGenerators,function(arg)
local   base,sgs,one,S,  T,  pnt, genlabels;

    if not Length(arg) in [2, 3]
        or not ForAll([1, 2], i -> IsHomogeneousList(arg[i])) then
      ErrorNoReturn("usage: ",
                    "StabChainBaseStrongGenerators(<base>, <sgs>[, <one>])");
    fi;
    base:=PlainListCopy(arg[1]);
    sgs:=PlainListCopy(arg[2]);
    if Length(arg)=3 then
      one:=arg[3];
    elif not IsEmpty(sgs) then
      one:= One(sgs[1]);
    else
      ErrorNoReturn("the identity element must be given as the third argument ",
                    "when the second argument <sgs> is empty");
    fi;
    S := EmptyStabChain( sgs, one );
    # Skip the identity in genlabels
    Assert(2, S.labels[1] = ());
    genlabels := PlainListCopy([2..Length(S.labels)]);
    T := S;
    for pnt  in base  do
        InsertTrivialStabilizer( T, pnt );
        T.genlabels := genlabels;
        T.generators := T.labels{T.genlabels};
        AGEST(T.orbit, T.genlabels, T.labels, T.translabels, T.transversal, T.genlabels);
        genlabels := Filtered(genlabels, x -> pnt^sgs[x] = pnt);
        T := T.stabilizer;
    od;

    return S;
end);

#############################################################################
##
#F  GroupStabChain( <arg> ) . . . . . . make (sub)group from stabilizer chain
##
InstallGlobalFunction(GroupStabChain,function( arg )
local   S,  G,  P,L;

    if Length( arg ) = 1  then
        S := arg[ 1 ];
        if not IsBound(S.generators) then
          G := GroupByGenerators( [], S.identity );
        else
          G := GroupByGenerators( S.generators, S.identity );
        fi;
    else
        P := arg[ 1 ];
        S := arg[ 2 ];
        if not IsBound(S.generators) then
          L := [];
        else
          L := S.generators;
        fi;
        if Length( arg ) = 3  and  arg[ 3 ] = true  then
            G := SubgroupNC( P, L );
        else
            G := Subgroup( P, L );
        fi;
    fi;
    SetStabChainMutable( G, S );

    return G;
end);

#############################################################################
##
#F  DepthSchreierTrees( <S> ) . . . . . . . . . . . . depth of Schreier trees
##
InstallGlobalFunction( DepthSchreierTrees, function( S )
    local   depths,  gens,  dep,  pnt,  sum,  i;

    depths := "";
    gens := [  ];
    while IsBound( S.stabilizer )  do
        UniteSet( gens, S.labels{ S.genlabels } );
        dep := [  ];  dep[ S.orbit[ 1 ] ] := -1;
        for pnt  in S.orbit  do
            dep[ pnt ] := dep[ pnt ^ S.transversal[ pnt ] ] + 1;
        od;
        sum := 0;
        for i  in dep  do
            sum := sum + i;
        od;
        i := sum / Length( S.orbit );
        Append( depths, Concatenation( String( Int( i ) ), "." ) );
        i := Int( 100 * ( i - Int( i ) ) );
        if i < 10  then
            Append( depths, "0" );
        fi;
        Append( depths, Concatenation( String( i ), "-",
                String( Maximum( Compacted( dep ) ) ), " " ) );
        S := S.stabilizer;
    od;
    Append( depths, Concatenation( String( Length( gens ) ), " gens" ) );
    return depths;
end );

#############################################################################
##
#F  AddGeneratorsExtendSchreierTree( <S>, <new> ) . . . . . .  add generators
##
##  This function may be called with a generatorless <S>.
##
InstallGlobalFunction( AddGeneratorsExtendSchreierTree, function( S, new )
    local   gen,  pos,  # new generator and its position in <S>.labels
            old,  ald,  # genlabels before extension
            newlabs,    # selected labels
            j;          # loop variable

    # Put in the new labels.
    old := BlistList( [ 1 .. Length( S.labels ) ], S.genlabels );
    old[ 1 ] := true;
    ald := StructuralCopy( old );
    newlabs := [];
    for gen  in new  do
        pos := Position( S.labels, gen );
        if pos = fail  then
            Add( S.labels, gen );
            Add( old, false );
            Add( ald, true );
            Add( S.genlabels, Length( S.labels ) );
            Add( newlabs, Length(S.labels));
        elif not ald[ pos ]  then
            Add( S.genlabels, pos );
            Add( newlabs, pos);
        fi;
        if     IsBound( S.generators )
           and pos <> 1 and not gen in S.generators  then
            Add( S.generators, gen );
        fi;
    od;

    # Extend the orbit and the transversal with the new labels.
#    len := Length( S.orbit );
 #   i := 1;



    #
    # New kernel functions take over from the GAP code in comments here.
    # the speedup is considerable.
    #

    # move tests outside loops as much as possible
    Assert(1,newlabs = Filtered(S.genlabels, j->not old[j]));

    if IsBound( S.cycles )  then
        AGESTC(S.orbit, newlabs, S.cycles, S.labels, S.translabels, S.transversal, S.genlabels);

        # for i in [1..len] do
        #     for j in newlabs do
        #         img := S.orbit[ i ] / S.labels[ j ];
        #         if IsBound( S.translabels[ img ] )  then
        #             S.cycles[i] := true;
        #         else
        #             S.translabels[ img ] := j;
        #             S.transversal[ img ] := S.labels[ j ];
        #             Add( S.orbit, img );
        #             Add( S.cycles, false);

        #         fi;
        #     od;
        # od;


        # while i <= Length( S.orbit )  do
        #     for j  in S.genlabels  do
        #         img := S.orbit[ i ] / S.labels[ j ];
        #         if  IsBound( S.translabels[ img ] )  then
        #             S.cycles[i] := true;
        #         else
        #             S.translabels[ img ] := j;
        #             S.transversal[ img ] := S.labels[ j ];
        #             Add( S.orbit, img );
        #             Add( S.cycles, false);

        #         fi;
        #     od;
        #     i := i + 1;
        # od;

    else

        AGEST(S.orbit, newlabs,  S.labels, S.translabels, S.transversal, S.genlabels);

        # for i in [1..len] do
        #     for j in newlabs do
        #         img := S.orbit[ i ] / S.labels[ j ];
        #         if not IsBound( S.translabels[ img ] )  then
        #             S.translabels[ img ] := j;
        #             S.transversal[ img ] := S.labels[ j ];
        #             Add( S.orbit, img );
        #         fi;
        #     od;
        # od;


        # while i <= Length( S.orbit )  do
        #     for j  in S.genlabels  do
        #         img := S.orbit[ i ] / S.labels[ j ];
        #             if not IsBound( S.translabels[ img ] )  then
        #                 S.translabels[ img ] := j;
        #                 S.transversal[ img ] := S.labels[ j ];
        #                 Add( S.orbit, img );
        #             fi;
        #         od;
        #     i := i + 1;
        # od;

    fi;
end );

#############################################################################
##
#F  ChooseNextBasePoint( <S>, <base>, <newgens> ) . . . . . . . . . . . local
##
InstallGlobalFunction( ChooseNextBasePoint, function( S, base, newgens )
    local   i,  pnt,  bpt,  pos;

    i := 1;
    while     i <= Length( base )
          and ForAll( newgens, gen -> base[ i ] ^ gen = base[ i ] )  do
        i := i + 1;
    od;
    if i <= Length( base )  then
        pnt := base[ i ];
    elif IsPermCollection( newgens )  then
        pnt := SmallestMovedPoint( newgens );
    else
        pnt := 1;
    fi;

    # If <pnt> is before  the base point <bpt>  of  <S>, insert a  new level.
    # `Before' means (1) <pnt> before <bpt> in <base> or (2) <pnt> in <base>,
    # <bpt> not in <base> or (3) <pnt> less than <bpt> both not in <base>.
    if IsBound( S.orbit )  then
        bpt := S.orbit[ 1 ];
        pos := Position( base, bpt );
    else
        bpt := infinity;
        pos := fail;
    fi;
    if    pos <> fail  and  i < pos              # (1)
       or pos =  fail  and  i <= Length( base )  # (2)
       or pos =  fail  and  pnt < bpt  then      # (3)
        InsertTrivialStabilizer( S, pnt );
        if IsBound( S.stabilizer.cycles )  then
            S.cycles := [ false ];
        elif IsBound( S.stabilizer.relativeOrders )  then
            Unbind( S.stabilizer.relativeOrders );
            Unbind( S.stabilizer.base           );
        fi;
    fi;

end );

#############################################################################
##
#F  StabChainStrong( <S>, <newgens>, <options> )  . . Schreier-Sims algorithm
##
InstallGlobalFunction( StabChainStrong, function( S, newgens, options )
    local   gen,        # one generator from <newgens>
            pnt,        # next base point to use
            len,        # length of orbit of <S>
            pnts,       # points to use for Schreier generators
            p,          # point in orbit of <S>
            rep, r, rr, # representative of <p>
            gen1, old,  # numbers of labels to be used for Schreier gens
            g,          # one of these labels
            sch,        # Schreier generator for '<S>.stabilizer'
            img,  i,  j;# loop variables

    # It is possible to prescribe a new operation domain for each level.
    if IsPermOnEnumerator( S.identity )  then
        newgens := List( newgens, gen -> PermOnEnumerator
                         ( Enumerator( S.identity ), gen ) );
    fi;

    # Determine the next base point.
    if IsBound( options.nextBasePoint )  then
        if not IsBound( S.orbit )  then
            pnt := options.nextBasePoint( S );
            InsertTrivialStabilizer( S, pnt );
        fi;
    else
        ChooseNextBasePoint( S, options.base, newgens );
    fi;

    # Add the new generators to <S>.
    pnt := S.orbit[ 1 ];
    len := Length( S.orbit );
    old := Length( S.genlabels );
    AddGeneratorsExtendSchreierTree( S, newgens );

    # If a new generator fixes the base point, put it into the stabilizer.
    for gen  in newgens  do
        if gen <> S.identity  and  pnt ^ gen = pnt  then
            StabChainStrong( S.stabilizer, [ gen ], options );
        fi;
    od;

    # Compute the Schreier generators (seems to work better backwards).
    if IsBound( S.cycles )  then
        pnts := ListBlist( [ 1 .. Length( S.orbit ) ], S.cycles );
    else
        pnts := [ 1 .. Length( S.orbit ) ];
    fi;
    gen1 := 1;
    for i  in Reversed( pnts )  do
        p := S.orbit[ i ];
        if IsBound( options.knownBase )  then
            rep := InverseRepresentativeWord( S, p );
        else
            rep := InverseRepresentative( S, p );
        fi;

        # Take only new generators for old, all generators for new points.
        if i <= len  then
            gen1 := old + 1;
        fi;
        for j  in [ gen1 .. Length( S.genlabels ) ]  do
          g := S.labels[ S.genlabels[ j ] ];

          # Avoid computing Schreier generators that will be trivial.
          if S.translabels[ p / g ] <> S.genlabels[ j ]  then

            # If a base is known, use it to test the Schreier generator.
            if IsBound( options.knownBase )  then
                if not MembershipTestKnownBase( S,
                           options.knownBase, [ rep, [ g ] ] )  then

                    # If this is the  first Schreier generator for this orbit
                    # point, multiply the representative.
                    if IsList( rep )  then
                        r := S.identity;
                        for rr  in rep  do
                            r := LeftQuotient( rr, r );
                        od;
                        rep := r;
                    fi;

                    sch := rep / g;
                    img := pnt ^ sch;
                    while img <> pnt  do
                        sch := sch * S.transversal[ img ];
                        img := img ^ S.transversal[ img ];
                    od;
                    StabChainStrong( S.stabilizer, [ sch ], options );
                fi;

            # If no  base is known, construct the  Schreier generator and put
            # it in the chain if it is non-trivial.
            else
                sch := SiftedPermutation( S, ( g * rep ) ^ -1 );
                if sch <> S.identity  then
                    StabChainStrong( S.stabilizer, [ sch ], options );
                fi;
            fi;

          fi;
        od;
    od;
end );


#############################################################################
##
#F  StabChainForcePoint( <S>, <pnt> ) . . . . . . . .  force <pnt> into orbit
##
InstallGlobalFunction( StabChainForcePoint, function( S, pnt )

    # Do nothing if <pnt> is already in the orbit of <S>.
    if    not IsBound( S.translabels )
       or not IsBound( S.translabels[ pnt ] )  then

        # If all generators of <S> fix <pnt>, insert a trivial stabilizer.
        if IsFixedStabilizer( S, pnt )  then
            InsertTrivialStabilizer( S, pnt );

        # Get  <pnt> in   the orbit   of   the stabilizer and  swap   the two
        # stabilizers.  If this  is  unsuccessful, the   stabilizer  chain is
        # incorrect, return `false' then.
        elif    not StabChainForcePoint( S.stabilizer, pnt )
             or not StabChainSwap( S )  then
            return false;
        fi;

    fi;
    return true;
end );

#############################################################################
##
#F  StabChainSwap( <S> )  . . . . . . . . . . . . . . .  swap two base points
##
InstallGlobalFunction( StabChainSwap, function( S )
    local   a,  b,      # basepoints that are to be switched
            T,  Tstab,  # copy of $S$ with $Tstab$ becomes $S_b$
            len,        # length of $Tstab.orbit$ to be reached
            pnt,        # one point from $a^S$ not yet in $a^{T_b}$
            ind,        # index of <pnt> in $S.orbit$
            img,        # image $b^{Rep(S,pnt)^-}$
            gen,        # new generator of $T_b$
            i;          # loop variable

    # get the two basepoints $a$ and $b$ that we have to switch
    a := S.orbit[ 1 ];
    b := S.stabilizer.orbit[ 1 ];

    # set $T = S$ and compute $b^T$ and a transversal $T/T_b$
    T := EmptyStabChain( S.labels, S.identity, b );
    Unbind( T.generators );
    AddGeneratorsExtendSchreierTree( T, S.generators );

    # initialize $Tstab$, which will become $T_b$
    Tstab := EmptyStabChain( [  ], S.identity, a );
    Unbind( Tstab.generators );
    AddGeneratorsExtendSchreierTree( Tstab,
            S.stabilizer.stabilizer.generators );

    # in the end $|b^T||a^{T_b}| = [T:T_{ab}] = [S:S_{ab}] = |a^S||b^{S_a}|$
    ind := 1;
    len := Length( S.orbit ) * Length( S.stabilizer.orbit )
           / Length( T.orbit );
    while Length( Tstab.orbit ) < len  do

        # choose a point $pnt \in a^S \ a^{T_b}$ with representative $s$
        repeat
            ind := ind + 1;

            # If <ind> exceeds the length of  <S>.orbit, <S> was an incorrect
            # stabilizer chain.
            if ind > Length( S.orbit )  then
                return false;
            fi;

            pnt := S.orbit[ ind ];
        until not IsBound( Tstab.translabels[ pnt ] );

        # find out what $s^-$ does with $b$ (without computing $s$!)
        img := b;
        i := pnt;
        while i <> a  do
            img := img ^ S.transversal[ i ];
            i   := i   ^ S.transversal[ i ];
        od;

        # if $b^{s^-}} \in b^{S_a}$ with representative $r \in S_a$
        if IsBound( S.stabilizer.translabels[ img ] )  then

            # with $gen = s^- r^-$ we have
            # $b^gen = {b^{s^-}}^{r^-} = img^{r-} = b$, so $gen \in S_b$
            # and $pnt^gen = {pnt^{s^-}}^{r^-} = a^{r-} = a$, so $gen$ is new
            gen := S.identity;
            while pnt ^ gen <> a  do
                gen := gen * S.transversal[ pnt ^ gen ];
            od;
            while b ^ gen <> b  do
                gen := gen * S.stabilizer.transversal[ b ^ gen ];
            od;
            AddGeneratorsExtendSchreierTree( Tstab, [ gen ] );

        fi;

    od;

    # copy everything back into the stabchain
    S.labels      := T.labels;
    S.genlabels   := T.genlabels;
    S.orbit       := T.orbit;
    S.translabels := T.translabels;
    S.transversal := T.transversal;
    if Length( Tstab.orbit ) = 1  then
        S.stabilizer := S.stabilizer.stabilizer;
    else
        S.stabilizer.labels      := Tstab.labels;
        S.stabilizer.genlabels   := Tstab.genlabels;
        if not IsBound(Tstab.generators) then
          Tstab.generators:=Tstab.labels{Tstab.genlabels};
        fi;
        S.stabilizer.generators  := Tstab.generators;
        S.stabilizer.orbit       := Tstab.orbit;
        S.stabilizer.translabels := Tstab.translabels;
        S.stabilizer.transversal := Tstab.transversal;
    fi;

    return true;
end );


#############################################################################
##
#F  LabsLims( <lab>, <hom>, <labs>, <lims> )  . . . .  help for next function
##
InstallGlobalFunction( LabsLims, function( lab, hom, labs, lims )
    local   pos;

    pos := Position( labs, lab );
    if pos = fail  then
        AddSet( labs, lab );
        pos := Position( labs, lab );
        if IsFunction( hom )  then
            Add(lims, hom(lab), pos);
        else
            Add(lims, lab ^ hom, pos);
        fi;
    fi;
    return lims[ pos ];
end );

#############################################################################
##
#F  ConjugateStabChain( <arg> ) . . . . . . . . .  conjugate stabilizer chain
##
InstallGlobalFunction( ConjugateStabChain, function( arg )
    local   S,  T,  hom,  map,  cond,           # arguments
            newlevs,                            # new labels lists
            len,                                # number of labels in <S>
            labels,  labpos,  orbit,  edges,    # conjugated components
            labs,  lims,                        # list of all labels/images
            img,  pnt,                          # image of label and point
            pos,  L,  l,  i;                    # loop variables

    # Get the arguments.
    S := arg[ 1 ];  T := arg[ 2 ];  hom := arg[ 3 ];  map := arg[ 4 ];
    if Length( arg ) > 4  then  cond := arg[ 5 ];
                          else  cond := S -> IsBound( S.stabilizer );  fi;
    newlevs := [  ];

    # Prepare common  lists for the labels and  their images at the different
    # levels.
    labs := [ S.identity ];
    lims := [ T.identity ];

    # Loop over the stabilizer chain.
    while cond( S )  do
        len := Length( S.labels );

        # If this is a  new  labels component, map  the  labels and mark  the
        # component so that it can be recognized at deeper levels.
        if len = 0  or  IsPerm( S.labels[ len ] )  then
            if IsPerm( hom )  then
                labels := OnTuples( S.labels, hom );
                labpos := [ 1 .. len ];
            else
                if IsIdenticalObj( S, T )  then  labels := [ T.identity ];
                                        else  labels := T.labels;        fi;
                labpos := ListWithIdenticalEntries( len, 0 );
                labpos[ 1 ] := 1;
            fi;
            Add( S.labels, rec( labels := labels,
                                labpos := labpos,
                             genlabels := Set( S.genlabels ) ) );
            Add( newlevs, S.labels );

        # The current labels component is not  new, so take the mapped labels
        # from the   mark  that was  inserted  when the  component  was first
        # encountered.
        else
            labels := S.labels[ len ].labels;
            labpos := S.labels[ len ].labpos;
            UniteSet( S.labels[ len ].genlabels, S.genlabels );
        fi;

        # Map the orbit and edges.
        edges := [  ];
        if IsPerm( map )  and  IsPerm( hom )  then
            orbit := OnTuples( S.orbit, map );
            edges{ orbit } := S.translabels{ S.orbit };
        else
            orbit := [  ];
            for pnt  in S.orbit  do
                if   IsFunction( map )  then  img := map( pnt );
                elif IsList    ( map )  then  img := map[ pnt ];
                else                          img := pnt ^ map;   fi;
                if not IsBound( edges[ img ] )  then
                    Add( orbit, img );
                    pos := labpos[ S.translabels[ pnt ] ];

                    # We can  already map  the  labels that appear   as edges
                    # because we know that their images  will be distinct and
                    # non-trivial.
                    if pos = 0  then
                        Add( labels, LabsLims( S.transversal[ pnt ], hom,
                                labs, lims ) );
                        pos := Length( labels );
                        labpos[ S.translabels[ pnt ] ] := pos;
                    fi;

                    edges[ img ] := pos;
                fi;
            od;
            if not IsPerm( hom )  then
                T.labpos := labpos;
            fi;
        fi;

        # Build a level of <T>  (`genlabels' will be completed when  `labpos'
        # is complete).
        T.labels      := labels;
        T.genlabels   := S.genlabels;
        T.orbit       := orbit;
        T.translabels := edges;
        T.transversal := [  ];
        T.transversal{ orbit } := labels{ edges{ orbit } };

        # Step down to the stabilizer.
        S := S.stabilizer;
        if not IsBound( T.stabilizer )  then
            T.stabilizer := EmptyStabChain( T.labels, T.identity );
        fi;
        T := T.stabilizer;

    od;

    # For   the distinct labels  components  of  the original  chain, map the
    # labels that  did  not appear     as edges  and   remove the   auxiliary
    # components.
    for L  in newlevs  do
        l := Last( L );
        i := Position( l.labpos, 0 );
        while i <> fail  do
            if i in l.genlabels  then
                img := LabsLims( L[ i ], hom, labs, lims );
                pos := Position( l.labels, img );
                if pos = fail  then
                    Add( l.labels, img );
                    pos := Length( l.labels );
                fi;
                l.labpos[ i ] := pos;
            fi;
            i := Position( l.labpos, 0, i );
        od;
        Remove( L );
    od;

    # Now that all labels have been mapped, complete  the `genlabels' and put
    # in `generators'.
    if not IsPerm( hom )  then
        L := arg[ 2 ];
        while IsBound( L.labpos )  do
            L.genlabels := Set( L.labpos{ L.genlabels } );
            RemoveSet( L.genlabels, 0 );
            RemoveSet( L.genlabels, 1 );
            Unbind( L.labpos );
            L := L.stabilizer;
        od;
    fi;
    L := arg[ 2 ];
    while IsBound( L.stabilizer )  do
        L.generators := L.labels{ L.genlabels };
        L := L.stabilizer;
    od;

    # Return the mapped stabilizer from the first level  where <cond> was not
    # satisfied (i.e., the ``end'' of the original chain).
    return T;

end );

#############################################################################
##
#F  ChangeStabChain(<G>,<base>[,<reduced>])  change/extend a stabilizer chain
##
##  reduced = -1    : extend stabilizer chain
##  reduced = false : change stabilizer chain, do not reduce it
##  reduced = true  : change stabilizer chain, reduce it
##
InstallGlobalFunction(ChangeStabChain,function( arg )
local   G,  base,  reduced,
        cnj,  S,  newBase,  old,  new,  i;

    # Get the arguments.
    G := arg[ 1 ];
    base := arg[ 2 ];
    if Length( arg ) > 2  then  reduced := arg[ 3 ];
                          else  reduced := true;      fi;

    cnj := G.identity;
    S := G;
    newBase := [  ];
    i := 1;
    while IsBound( S.stabilizer )  or  i <= Length( base )  do
        old := BasePoint( S );

        # Cut off unwanted trivial stabilizers at the end.
        if     Length( S.genlabels ) = 0
           and ( reduced = true  or  i > Length( base ) )  then
            RemoveStabChain( S );
            i := Length( base ) + 1;

        # Determine the new base point for this level.
        elif i <= Length( base )  then
            new := base[ i ] / cnj;
            i := i + 1;

            # Stabilizer chain extension.
            if reduced = -1  then
                AddSet( newBase, new );
                if new <> old  then
                    if IsFixedStabilizer( S, new )  then
                        InsertTrivialStabilizer( S, new );
                    else
                        Error("<base> must be an extension of base of <G>");
                    fi;
                fi;
                S := S.stabilizer;

            # Base change. Return `false' if <S> turns out to be incorrect.
            elif reduced = false  or  not IsFixedStabilizer( S, new )  then
                if IsBound( S.stabilizer )  then
                    if not StabChainForcePoint( S, new )  then
                        return false;
                    fi;
                    cnj := LeftQuotient( InverseRepresentative( S, new ),
                                   cnj );
                else
                    InsertTrivialStabilizer( S, new );
                fi;
                AddSet( newBase, S.orbit[ 1 ] );
                S := S.stabilizer;
            fi;

        # Delete unwanted trivial  stabilizers  (e.g., double  points  in the
        # base).
        elif    old in newBase
             or reduced = true  and  Length( S.orbit ) = 1  then
            S.labels     := S.stabilizer.labels;
            S.genlabels  := S.stabilizer.genlabels;
            S.generators := S.stabilizer.generators;
            if IsBound( S.stabilizer.orbit )  then
                S.orbit       := S.stabilizer.orbit;
                S.translabels := S.stabilizer.translabels;
                S.transversal := S.stabilizer.transversal;
            else
                Unbind( S.orbit );
                Unbind( S.translabels );
                Unbind( S.transversal );
            fi;
            if IsBound( S.stabilizer.stabilizer )  then
                S.stabilizer := S.stabilizer.stabilizer;
            else
                Unbind( S.stabilizer );
            fi;

        # Simply move down the stabilizer chain (to look for double points).
        else
            AddSet( newBase, old );
            S := S.stabilizer;
        fi;

    od;

    # Conjugate to move all the points to the beginning of their orbit.
    if cnj <> S.identity  then
        ConjugateStabChain( G, G, cnj, cnj );
    fi;

    return true;
end);

#############################################################################
##
#F  ExtendStabChain( <S>, <base> )  . . . . . . . . extend a stabilizer chain
##
InstallGlobalFunction(ExtendStabChain,function( S, base )
    ChangeStabChain( S, base, -1 );
end);


#############################################################################
##
#F  ReduceStabChain( <S> )  . . . . . . . . . . . . reduce a stabilizer chain
##
InstallGlobalFunction(ReduceStabChain,function( S )
    ChangeStabChain( S, [  ], true );
end);

#############################################################################
##
#F  EmptyStabChain( <labels>,<id>[,<limgs>,<idimg>][,<pnt>] ) . .  stab chain
##
InstallGlobalFunction(EmptyStabChain,function( arg )
local   S;

    S := rec(  labels := arg[ 1 ],
            genlabels := [  ],
           generators := [  ],
             identity := arg[ 2 ] );
    if Length( S.labels ) = 0  or  S.labels[ 1 ] <> S.identity  then
        Add( S.labels, S.identity, 1);
    fi;
    if Length( arg ) >= 4  then
        S.labelimages := arg[ 3 ];
        S.genimages   := [  ];
        S.idimage     := arg[ 4 ];
        if Length( S.labelimages ) = 0  then
            Add( S.labelimages, S.idimage );
        fi;
    fi;
    if Length( arg ) mod 2 = 1  then
        InitializeSchreierTree( S, Last( arg ) );
    fi;
    return S;
end);

#############################################################################
##
#F  InitializeSchreierTree( <S>, <pnt> )  . . . .  initialize a Schreier tree
##
InstallGlobalFunction( InitializeSchreierTree, function( S, pnt )
    S.orbit       := [ pnt ];
    S.translabels := [  ];  S.translabels[ pnt ] := 1;
    S.transversal := [  ];  S.transversal[ pnt ] := S.identity;
    if IsBound( S.idimage )  then
        S.transimages := [  ];  S.transimages[ pnt ] := S.idimage;
    fi;
end );

#############################################################################
##
#F  InsertTrivialStabilizer( <S>, <pnt> ) . .  add redundant base point <pnt>
##
InstallGlobalFunction( InsertTrivialStabilizer, function( S, pnt )
    S.stabilizer := ShallowCopy( S );
    S.genlabels  := ShallowCopy( S.stabilizer.genlabels );
    if IsBound( S.generators )  then
        S.generators := ShallowCopy( S.stabilizer.generators );
        if IsBound( S.idimage )  then
            S.genimages := ShallowCopy( S.stabilizer.genimages );
        fi;
    fi;
    InitializeSchreierTree( S, pnt );
end );

#############################################################################
##
#F  RemoveStabChain( <S> )  . . . . . . . .  cut off rest of stabilizer chain
##
InstallGlobalFunction(RemoveStabChain,function( S )
local  name;

    for name  in RecNames( S )  do
        if name <> "identity"  and  name <> "idimage"  then
            Unbind( S.( name ) );
        fi;
    od;
    S.labels     := [ S.identity ];
    S.genlabels  := [  ];
    S.generators := [  ];
end);

#############################################################################
##
#F  BasePoint( <S> )  . . . . . . . . . . . . . . . . . . . base point of <S>
##
InstallGlobalFunction( BasePoint, function( S )
    if IsBound( S.orbit )  then  return S.orbit[ 1 ];
                           else  return false;         fi;
end );

#############################################################################
##
#F  IsInBasicOrbit( <S>, <pnt> )  . . . . . . . . .  is <pnt> in basic orbit?
##
InstallGlobalFunction( IsInBasicOrbit, function( S, pnt )
    return     IsBound( S.translabels )
           and IsBound( S.translabels[ pnt ] );
end );

#############################################################################
##
#F  IsFixedStabilizer( <S>, <pnt> ) . . . . . . . . .  is <pnt> fixed by <S>?
##
InstallGlobalFunction( IsFixedStabilizer, function( S, pnt )
    return ForAll( S.generators, gen -> pnt ^ gen = pnt );
end );

#############################################################################
##
#F  InverseRepresentative( <S>, <pnt> ) . .  perm mapping <pnt> to base point
##
InstallGlobalFunction( InverseRepresentative, function( S, pnt )
local   bpt,  rep,te;

    bpt := S.orbit[ 1 ];
    rep := S.identity;
    while pnt <> bpt  do
        te:=S.transversal[pnt];
        pnt:=pnt^te;
        rep := rep * te;
    od;
    return rep;
end );

#############################################################################
##
#F  QuickInverseRepresentative( <S>, <pnt> )  . . . . . . . same, but quicker
##
InstallGlobalFunction( QuickInverseRepresentative, function( S, pnt )
local   bpt,  rep,  lab,  pow;

    bpt := S.orbit[ 1 ];
    rep := S.identity;
    lab := S.translabels[ pnt ];
    pow := 1;
    while pnt <> bpt  do
        pnt := pnt ^ S.transversal[ pnt ];
        if S.translabels[ pnt ] = lab  then
            pow := pow + 1;
        else
            rep := rep * S.labels[ lab ] ^ pow;
            lab := S.translabels[ pnt ];
            pow := 1;
        fi;
    od;
    return rep;
end );

#############################################################################
##
#F  InverseRepresentativeWord( <S>, <pnt> ) . . . . . . . inverse rep as word
##
InstallGlobalFunction( InverseRepresentativeWord, function( S, pnt )
local   word,  bpt;

    word := [  ];
    bpt := S.orbit[ 1 ];
    while pnt <> bpt  do
        Add( word,   S.transversal[ pnt ] );
        pnt := pnt ^ S.transversal[ pnt ];
    od;
    return word;
end );

#############################################################################
##
#F  SiftedPermutation( <S>, <g> ) . . . . . . . . . . . .  sifted permutation
##
##  This function may be called with a generatorless <S>.
##
InstallGlobalFunction(SiftedPermutation,function( S, g )
local   bpt,  img;

    # The  following     condition   tests     `IsBound(S.stabilizer)',   not
    # `IsEmpty(S.genlabels)'. This is necessary because  the function may  be
    # called with an inconsistent chain from `NormalClosure'.
    while     IsBound( S.stabilizer )
          and g <> S.identity  do
        bpt := S.orbit[ 1 ];
        img := bpt ^ g;
        if IsBound( S.transversal[ img ] )  then
            while img <> bpt  do
                g := g * S.transversal[ img ];
                img := bpt ^ g;
            od;
            S := S.stabilizer;
        else
            return g;
        fi;
    od;
    return g;
end);

#############################################################################
##
#F  MinimalElementCosetStabChain( <S>, <g> )  . . .  minimal element of coset
##
##  This function may be called with a generatorless <S>.
##
InstallGlobalFunction(MinimalElementCosetStabChain,function( S, g )
local   p,i,a,bp,pp;

    while not IsEmpty( S.genlabels )  do

        if IsPlistRep(S.orbit) and IsPosInt(S.orbit[1])
          and IsInternalRep(g) then
          p:=SMALLEST_IMG_TUP_PERM(S.orbit,g);
        else
          p:=infinity;
          for i in S.orbit do
            a:=i^g;
            if a<p then
              p:=a;
            fi;
          od;
        fi;

        bp:=S.orbit[1];
        pp:=p/g;
        while bp<>pp  do
            g:=LeftQuotient(S.transversal[pp],g);
            pp:=p/g;
        od;
#        while S.orbit[ 1 ] ^ g <> p  do
#            g := LeftQuotient( S.transversal[ p / g ], g );
#        od;
        S := S.stabilizer;
    od;
    return g;
end);

#############################################################################
##
#M  MembershipTestKnownBase( <S>, <knownBase>, <word> ) . . . with known base
##
##  This function may be called with a generatorless <S>.
##
InstallMethod( MembershipTestKnownBase, "stabchain, base, word",true,
  [ IsRecord, IsList and IsCyclotomicCollection, IsList ], 0,
    function( S, knownBase, word )
    local   base,  g,  i,  j,  bpt;

    base := Concatenation( BaseStabChain( S ), knownBase );
    for g  in word  do
        if IsPerm( g )  then
            base := OnTuples( base, g );
        else
            for i  in Reversed( [ 1 .. Length( g ) ] )  do
                for j  in [ 1 .. Length( base ) ]  do
                    base[ j ] := base[ j ] / g[ i ];
                od;
            od;
        fi;
    od;
    while     not IsEmpty( S.genlabels )
          and IsBound( S.translabels[ base[ 1 ] ] )  do
        bpt := S.orbit[ 1 ];
        while base[ 1 ] <> bpt  do
            base := OnTuples( base, S.transversal[ base[ 1 ] ] );
        od;
        base := base{ [ 2 .. Length( base ) ] };
        S := S.stabilizer;
    od;
    return base = knownBase;
end );

InstallOtherMethod( MembershipTestKnownBase, true, [ IsRecord,
        IsGroup, IsPerm ], 0,
    function( S, G, t )
    return SiftedPermutation( S, t ) = S.identity;
end );

# the base `BaseOfGroup' does not need to confirm to the stabilizer chain
# `S'. therefore the following method is invalid. AH
#InstallOtherMethod( MembershipTestKnownBase, true, [ IsRecord,
#        IsGroup and HasBaseOfGroup, IsPerm ], 0,
#    function( S, G, t )
#    Error("this method may not be used!");
#    return MembershipTestKnownBase( S, BaseOfGroup( G ), [ t ] );
#end );

#############################################################################
##
#F  BaseStabChain( <S> )  . . . . . . . . . . . . . . . . . . . . . . .  base
##
##  This function may be called with a generatorless <S>.
##
InstallGlobalFunction(BaseStabChain,function( S )
    local   base;

    base := [  ];
    while IsBound( S.stabilizer )  do
        Add( base, S.orbit[ 1 ] );
        S := S.stabilizer;
    od;
    return base;
end);

#############################################################################
##
#F  SizeStabChain( <S> )  . . . . . . . . . . . . . . . . . . . . . . .  size
##
##  This function may be called with a generatorless <S>.
##
InstallGlobalFunction(SizeStabChain,function( S )
    local   size;

    size := 1;
    while not IsEmpty( S.genlabels )  do
        size := size * Length( S.orbit );
        S := S.stabilizer;
    od;
    return size;
end);

#############################################################################
##
#F  StrongGeneratorsStabChain( <S> )  . . . . . . . . . . . strong generators
##
InstallGlobalFunction(StrongGeneratorsStabChain,function( S )
    local   sgs;

    sgs := [  ];
    while not IsEmpty( S.generators )  do
        UniteSet( sgs, S.generators );
        S := S.stabilizer;
    od;
    return sgs;
end);

#############################################################################
##
#F  IndicesStabChain( <S> ) . . . . . . . . . . . . . . . . . . . . . indices
##
##  This function may be called with a generatorless <S>.
##
InstallGlobalFunction(IndicesStabChain,function( S )
    local   ind;

    ind := [  ];
    while IsBound( S.stabilizer )  do
        Add( ind, Length( S.orbit ) );
        S := S.stabilizer;
    od;
    return ind;
end);

#############################################################################
##
#F  ListStabChain( <S> )  . . . . . . . . . . . . .  stabilizer chain as list
##
##  This function may be called with a generatorless <S>.
##
InstallGlobalFunction(ListStabChain,function( S )
    local   list;

    list := [  ];
    while IsBound( S.stabilizer )  do
        Add( list, S );
        S := S.stabilizer;
    od;
    Add( list, S );
    return list;
end);

#############################################################################
##
#F  OrbitStabChain( <S>, <pnt> )  . . . . . . . . . orbit of stabilizer chain
##
InstallGlobalFunction(OrbitStabChain,function( S, pnt )
    if IsBound( S.edges )  and  IsBound( S.edges[ pnt ] )  then
        return ShallowCopy( S.orbit );
    else
        return OrbitPerms( S.generators, pnt );
    fi;
end);

#############################################################################
##
#M  MinimalStabChain( <G> ) . . . . . . . . . . . .  minimal stabilizer chain
##
InstallMethod( MinimalStabChain, "Perm", true, [ IsPermGroup] , 0,
    function( G )
    return StabChainOp( G, rec( base := [ 1 .. LargestMovedPoint( G ) ] ) );
end );


#############################################################################
##
#F  SCMinSmaGens(<G>,<S>,<emptyset>,<identity element>,<flag>)
##
##  This function computes a stabilizer chain for a minimal base image and
##  a smallest generating set wrt. this base for a permutation
##  group.
##
##  <G> must be a permutation group and <S> a mutable stabilizer chain for
##  <G> that defines a base <bas>. Let <mbas> the smallest image (OnTuples)
##  of <G>. Then this operation changes <S> to a stabilizer chain wrt.
##  <mbas>.
##  The arguments <emptyset> and <identity element> are needed
##  only for the recursion.
##
##  The function returns a record whose component `gens' is a list whose
##  first element is the smallest element wrt. <bas>. (i.e. an element which
##  maps <bas> to <mbas>. If <flag> is `true', `gens' is  the smallest
##  generating set wrt. <bas>. (If <flag> is `false' this will not be
##  computed.)
InstallGlobalFunction(SCMinSmaGens,function (G,S,bas,pre,flag)
local   Sgens,      # smallest generating system of <S>, result
        gens,       # smallest generating system of <S>, result
        span,       # <gens>
        stb,        # Stab_span(bas{1..n-1})
        min,        # minimum in orbit
        nbas,       # bas+[min]
        rep,        # representative mapping minimal
        gen,        # one generator in <gens>
        orb,        # basic orbit of <S>
        pnt,        # one point in <orb>
        T;          # stabilizer in <S>

  Sgens:=S.generators;
  # handle the anchor case
  if Length(Sgens) = 0  then
      return rec(gens:=[pre],span:=SubgroupNC(Parent(G),[pre]));
  fi;

  # the new ``base'' point is the point to which the current level base
  # point is mapped under pre.
  pnt:=S.orbit[1];

  # find a representative that moves the point as small as possible
  rep:=S.identity;
  min:=Minimum(S.orbit);
  nbas:=Concatenation(bas,[min]);
  while pnt<>min do
    gen:=S.transversal[min];
    rep:=LeftQuotient(gen,rep);
    min:=min^gen;
  od;

  # now we want orbit and stabilizer wrt. this point.
  ConjugateStabChain(S,S,rep,rep);

  # this element will have to be pre-multiplied to all generators
  # generated on this or lower level
  pre:=pre*rep;

  # recursive call to change base below and compute smallest mapping
  # generators there
  gens:=SCMinSmaGens(G,S.stabilizer,nbas,pre,flag);

  # do we want to compute the minimal generating set?
  if flag=false then
    return rec(gens:=[pre]);
  fi;

  span:=gens.span;
  gens:=gens.gens;
  pre:=gens[1]; # the smallest generators is the premul. element

  # get the sorted orbit (the basepoint will be the first point)
  orb := Set( S.orbit );

  # compute the stabilizer in `span' of the first base points (that's the
  # group we're extending at this level)
  stb:=Stabilizer(span,bas,OnTuples);

  # this stabilizer will cover already some points
  SubtractSet( orb, Orbit( stb, S.orbit[1]));

  # handle the points in the orbit
  while Length(orb) > 0  do

    # take the smallest remaining point (coset) and get one representative
    # for it
    pnt := orb[1];
    gen := S.identity;
    while S.orbit[1] ^ gen <> pnt  do
        gen := LeftQuotient( S.transversal[ pnt / gen ], gen );
    od;

    # now change gen by elements in the lower stabilizers  to
    # find the minimal element in its coset.
    T := S.stabilizer;
    while Length(T.generators) <> 0  do
      pnt := Minimum( OnTuples( T.orbit, gen ) );
      while T.orbit[1] ^ gen <> pnt  do
        gen := LeftQuotient( T.transversal[ pnt / gen ], gen );
      od;
      T := T.stabilizer;
    od;

    # pre-multiply with the element mapping the base to the smallest base
    gen:=pre*gen;

    # add this generator to the generators list
    Add( gens, gen );
    #NC is safe -- always use parent(G)
    span:=ClosureSubgroupNC(span,gen);
    stb:=Stabilizer(span,bas,OnTuples);

    # test which cosets we can now cover: reduce orbit
    SubtractSet( orb, Orbit( stb, S.orbit[1]));

  od;

  # return the smallest generating system
  return rec(gens:=gens,span:=span);
end);


#############################################################################
##
#F  LargestElementStabChain(<S>,<id>)
##
InstallGlobalFunction(LargestElementStabChain,function(S,rep)
local   min,    # minimum in orbit
        pnt,    # one point in <orb>
        i,      # loop
        val,    # point image
        gen;    # gen. in transversal

  # handle the anchor case
  if Length(S.generators) = 0  then
      return rep;
  fi;

  # the new ``base'' point is the point to which the current level base
  # point is mapped under pre.
  pnt:=S.orbit[1];

  # find a representative that moves the point as large as possible
  min:=0;
  val:=0;
  for i in S.orbit do
    if i^rep>val then
      min:=i;
      val:=i^rep;
    fi;
  od;

  while pnt<>min do
    gen:=S.transversal[min];
    rep:=LeftQuotient(gen,rep);
    min:=min^gen;
  od;

  # recursive call to change base below and compute smallest mapping
  # generators there
  return LargestElementStabChain(S.stabilizer,rep);

end);

#############################################################################
##
#F  ElementsStabChain(<S>)
##
InstallGlobalFunction(ElementsStabChain,function ( S )
    local   elms,               # element list, result
            stb,                # elements of the stabilizer
            pnt,                # point in the orbit of <S>
            rep;                # inverse representative for that point

    # if <S> is trivial then it is easy
    if Length(S.generators) = 0  then
        elms := [ S.identity ];

    # otherwise
    else

        # start with the empty list
        elms := [];

        # compute the elements of the stabilizer
        stb := ElementsStabChain( S.stabilizer );

        # loop over all points in the orbit
        for pnt  in S.orbit  do

           # add the corresponding coset to the set of elements
           rep := S.identity;
           while S.orbit[1] ^ rep <> pnt  do
                rep := LeftQuotient( S.transversal[pnt/rep], rep );
           od;
           Append( elms, stb * rep );

        od;

   fi;

   # return the result
   return elms;
end);

#############################################################################
##
#F  IteratorStabChain(<S>)
##
InstallGlobalFunction( NextIterator_StabChain,
function(iter)
    local l, re;

    if iter!.state = 0 then
        if Length(iter!.pos) = 0 then
            iter!.state := 2;
        else
            iter!.state := 1;
        fi;
        return ();
    elif iter!.state = 1 then
        l := Length(iter!.stack);
        # Identity is special cased since we only want to
        # produce a new element when NextIter is called
        while (l > 0) and iter!.pos[l] = Length(iter!.stack[l].orbit) do
            l := l - 1;
        od;

        # Advance, and check whether we have exhausted all group
        # elements
        iter!.pos[l] := iter!.pos[l] + 1;

        if l = Length(iter!.stack) and (iter!.pos = iter!.epos) then
            iter!.state := 2;
        fi;

        # Now we first find the correct representative for
        # this element
        re := InverseRepresentative(iter!.stack[l], iter!.stack[l].orbit[iter!.pos[l]]);
        if l = 1 then
            iter!.rep[l] := re^(-1);
        else
            iter!.rep[l] := LeftQuotient( re, iter!.rep[l-1]);
        fi;
        l := l + 1;
        while l <= Length(iter!.stack) do
            iter!.rep[l] := iter!.rep[l-1];
            iter!.pos[l] := 1;
            l := l + 1;
        od;
        return iter!.rep[l-1];
    fi;
end);

InstallGlobalFunction(IteratorStabChain,
function(S)
    local r,lstack;

    lstack := ListStabChain(S);
    Remove(lstack);
    while Length(lstack) > 0 and Length(Last(lstack).orbit) = 1 do
        Remove(lstack);
    od;
    r := rec (
          stack := lstack
        , pos := List(lstack, x -> 1)
        , epos := List(lstack, x -> Length(x.orbit))
        , rep := List(lstack, x -> ())
        , state := 0
        , NextIterator := NextIterator_StabChain
        , IsDoneIterator := iter -> (iter!.state = 2)
        , ShallowCopy := iter -> rec( stack := iter!.stack
                                    , pos := ShallowCopy(iter!.pos)
                                    , epos := iter!.epos
                                    , rep := ShallowCopy(iter!.rep)
                                    , state := iter!.state
                                    )
    );
    return IteratorByFunctions(r);
end);

InstallMethod( ViewObj,"stabilizer chain records", true,
  [ IsRecord ], 0,
function(r)
local sz;
  if not (IsBound(r.stabilizer) and IsBound(r.generators) and
          IsBound(r.orbit) and IsBound(r.identity) and
          IsBound(r.transversal)) then
    TryNextMethod();
  fi;
  sz:= SizeStabChain(r);

  Print("<stabilizer chain record, Base ",BaseStabChain(r),
        ", Orbit length ",Length(r.orbit),", Size: ",sz,">");
end);

[ Konzepte0.61Was zu einem Entwurf gehört  Wie die Entwicklung von Software durchgeführt wird  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge