Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/GAP/lib/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 18.9.2025 mit Größe 111 kB image not shown  

SSL tom.gi   Sprache: unbekannt

 
#############################################################################
##
##  This file is part of GAP, a system for computational discrete algebra.
##  This file's authors include Götz Pfeiffer, Thomas Merkwitz.
##
##  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
##
##  This file contains methods for tables of marks.
##
##  1. Tables of Marks
##  2. More about Tables of Marks
##  3. Table of Marks Objects in {\GAP}
##  4. Constructing Tables of Marks
##  5. Printing Tables of Marks
##  6. Sorting Tables of Marks
##  7. Technical Details about Tables of Marks
##  8. Attributes of Tables of Marks
##  9. Properties of Tables of Marks
##  10. Other Operations for Tables of Marks
##  11. Accessing Subgroups via Tables of Marks
##  12. The Interface between Tables of Marks and Character Tables
##  13. Generic Construction of Tables of Marks
##


#############################################################################
##
##  4. Constructing Tables of Marks
##


#############################################################################
##
#F  GeneratorsListTom( <G>, <classes> ) . . . . . . . . . . create generators
##
##  `GeneratorsListTom' lists a set of generators for a representative
##  of each conjugacy class of subgroups.
##
BindGlobal( "GeneratorsListTom", function( G, classes )
    local sub, gen, res;

    # take the generators
    sub:= List( classes, x -> GeneratorsOfGroup( Representative( x ) ) );

    # form the generators list
    gen:= Union( sub );

    # compute the positions
    res:= List( sub, grp -> List( grp, elm -> Position( gen, elm ) ) );
    return [ gen, res ];
    end );


#############################################################################
##
#M  TableOfMarks( <G> ) . . . . . . . . compute the table of marks of a group
##
InstallMethod( TableOfMarks,
    "for a cyclic group",
    [ IsGroup and IsCyclic ],
    function( G )
    local n, c, tom, gens, gen, subs, marks, classNames,
          name, i, j, divs, index;

    n:= Size( G );

    # construct the table of marks without the group

    # initialize
    divs:= DivisorsInt( n );
    c:= Length( divs );
    subs:= [];
    marks:= [];
    classNames:=[];

    # Compute generators for each subgroup.
    gens:= GeneratorsOfGroup( G );
    if 1 < Length( gens ) then
      gens:= MinimalGeneratingSet( G );
    fi;
    if 0 < Length( gens ) then
      gen:= gens[1];
    else
      gen:= One( G );
    fi;
    gens:= [ List( divs, d -> gen^(n/d) ),
             List( [ 1 .. c ], i -> [ i ] ) ];

    # construct each subgroup (each divisor)
    for i in [ 1 .. c ] do

      classNames[i]:= String( divs[i] );
      ConvertToStringRep( classNames[i] );

      index:= n / divs[i];
      subs[i]:= [];
      marks[i]:= [];
      for j in [1..i] do
        if divs[i] mod divs[j] = 0 then
          Add( subs[i], j );
          Add( marks[i], index );
        fi;
      od;

    od;

    # add new components
    if HasName( G ) then
      name:= Name( G );
    else
      name:= Concatenation( "C", String( n ) );
    fi;

    # make the object
    tom:= rec( Identifier                := name,
               SubsTom                   := subs,
               MarksTom                  := marks,
               NormalizersTom            := List( [ 1 .. c ], x -> c ),
               DerivedSubgroupsTomUnique := List( [ 1 .. c ], x -> 1 ),
               UnderlyingGroup           := G,
               GeneratorsSubgroupsTom    := gens );

    tom:= ConvertToTableOfMarks( tom );
    SetClassNamesTom( tom, classNames );
    return tom;
    end );


#############################################################################
##
#F  TableOfMarksByLattice( <G> )
##
InstallGlobalFunction( TableOfMarksByLattice, function( G )
    local marks,             # components of the table of marks
          subs,
          normalizers,
          derivedSubgroups,
          tom,
          mrks,              # marks for one class
          ind,               # index of <I> in <N>
          zuppos,            # generators of prime power order
          classes,           # list of all classes
          classesZups,       # zuppos blist of classes
          I,                 # representative of a class
          Ielms,             # elements of <I>
          Izups,             # zuppos blist of <I>
          N,                 # normalizer of <I>
          D,                 # derived subgroup of <I>,
          Delms,             # elements of <D>,
          Dzups,             # zuppos blist of <D>
          DG,                # derived subgroup of <G>
          DGzups,            # zuppos blist of <DG>
          Jzups,             # zuppos of a conjugate of <I>
          Kzups,             # zuppos of a representative in <classes>
          reps,              # transversal of <N> in <G>
          i,k,r;           # loop variables

#T Is this necessary at all?
    LatticeSubgroups( G );

    # compute the lattice,fetch the classes,zuppos,and representatives
    classes:= ShallowCopy( ConjugacyClassesSubgroups( G ) );

    # sort the classes
    SortBy(classes,a->Size(Representative(a)));
    classesZups:=[];

    # compute a system of generators for the cyclic sgr. of prime power size
    zuppos:=Zuppos(G);

    # initialize the table of marks
    Info(InfoLattice,1,"computing table of marks");
    subs:=List([1..Length(classes)],x->[]);
    marks:=List([1..Length(classes)],x->[]);
    derivedSubgroups:=[];
    normalizers:=[];
    DG:= DerivedSubgroup( G );
    if Size(DG) = Size(G) then   # G perfect
        derivedSubgroups[Length(classes)]:= Length(classes);
    elif Size(DG) = 1 then       # G abelian
        derivedSubgroups[Length(classes)]:= 1;
    else
        DGzups:=BlistList(zuppos,AttributeValueNotSet(AsList,DG));
    fi;
    Unbind(DG);

    # loop over all classes
    for i  in [1..Length(classes)-1]  do

      # take the subgroup <I>
      I:=Representative(classes[i]);

      # compute the zuppos blist of <I>
      Ielms:=AttributeValueNotSet(AsList,I);
      Izups:=BlistList(zuppos,Ielms);
      classesZups[i]:=Izups;

      # compute the normalizer of <I>
      N:=Normalizer(G,I);
      ind:=Size(N)/Size(I);
      if Size(N)=Size(I) then  # <I> selfnormalizing
        normalizers[i]:=i;
      elif Size(N)=Size(G) then # <I> normal
        normalizers[i]:=Length(classes);
      else
        normalizers[i]:=BlistList(zuppos,
                          AttributeValueNotSet(AsList,N));
      fi;

      # compute the derived subgroup
      D:= AttributeValueNotSet( DerivedSubgroup, I );
      if Size(D) = Size(I) then  # <I> perfect
        derivedSubgroups[i]:=i;
      elif Size(D) = 1 then      # <I> abelian
        derivedSubgroups[i]:=1;
      else
        Delms:=AttributeValueNotSet(AsList,D);
        Dzups:=BlistList(zuppos,Delms);
      fi;

      # compute the right transversal (but don't store it)
      reps:=RightTransversalOp(G,N);

      # set up the marking list
      mrks   :=ListWithIdenticalEntries(Length(classes),0);
      mrks[1]:=Length(reps) * ind;
      mrks[i]:=1 * ind;

      # loop over the conjugates of <I>
      for r  in [1..Length(reps)]  do

        # compute the zuppos blist of the conjugate
        if reps[r] = One(G) then
          Jzups:=Izups;
        else
          Jzups:=BlistList(zuppos,OnTuples(Ielms,reps[r]));
          if not IsBound(derivedSubgroups[i]) then
            Dzups:=  BlistList(zuppos,OnTuples(Delms,reps[r]));
          fi;
        fi;

        #look if the conjugate of <I> is the normalizer of a smaller
        #class
        for k in [2..i-1] do
          if normalizers[k]=Jzups then
            normalizers[k]:=i;
          fi;
        od;

        # look if it is the derived subgroup of G
        if IsBound(DGzups) and DGzups = Jzups then
          derivedSubgroups[Length(classes)]:=i;
          Unbind(DGzups);
        fi;

        # loop over all other (smaller classes)
        for k  in [2..i-1]  do
          Kzups:=classesZups[k];

          #test if the <K> is the derived subgroup of <J>
          if not IsBound(derivedSubgroups[i]) and Kzups = Dzups then
            derivedSubgroups[i]:=k;
            Unbind(Dzups);
          fi;

          # test if the <K> is a subgroup of <J>
          if IsSubsetBlist(Jzups,Kzups)  then
            mrks[k]:=mrks[k] + ind;
          fi;

        od;

      od;

      # compress this line into the table of marks
      for k  in [1..i]  do
        if mrks[k] <> 0  then
          Add(subs[i],k);
          Add(marks[i],mrks[k]);
        fi;
      od;

      Unbind(Ielms);
      Unbind(Delms);
      Unbind(reps);
      Info( InfoLattice, 2,
            "testing class ",i,", size = ",Size(I),
            ", length = ",Size(G) / Size(N),", includes ",
            Length(marks[i])," classes");

    od;

    # Handle the whole group.
    Info( InfoLattice,2,"testing class ",Length(classes),", size = ",
          Size(G), ", length = ",1,", includes ",
          Length(marks[Length(classes)])," classes");
    subs[Length(classes)]:=[1..Length(classes)] + 0;
    marks[Length(classes)]:=ListWithIdenticalEntries(Length(classes),1);
    normalizers[Length(classes)]:=Length(classes);

    # Make the object.
    tom:= rec( SubsTom                   := subs,
               MarksTom                  := marks,
               NormalizersTom            := normalizers,
               DerivedSubgroupsTomUnique := derivedSubgroups,
               UnderlyingGroup           := G,
               GeneratorsSubgroupsTom    := GeneratorsListTom( G, classes ) );
    ConvertToTableOfMarks( tom );
    if HasName( G ) then
      SetIdentifier( tom, Name( G ) );
    fi;

    return tom;
end );


InstallMethod( TableOfMarks,
    "for a group with lattice",
    [ IsGroup and HasLatticeSubgroups ], 10,
    TableOfMarksByLattice );

InstallMethod( TableOfMarks,
    "for solvable groups (call `LatticeSubgroups' and use the lattice)",
    [ IsSolvableGroup ],
    TableOfMarksByLattice );

InstallMethod( TableOfMarks,
    "cyclic extension method",
    [ IsGroup ],
    function( G )
    local factors,           # factorization of <G>'s size
          zuppos,            # generators of prime power order
          ll,
          zupposPrime,       # corresponding prime
          zupposPower,       # index of power of generator
          nrClasses,         # number of classes
          classesZups,       # zuppos blist of classes
          classesExts,       # extend-by blist of classes
          perfect,           # classes of perfect subgroups of <G>
          perfectNew,        # this class of perfect subgroups is new
          perfectZups,       # zuppos blist of perfect subgroups
          layerb,            # begin of previous layer
          layere,            # end of previous layer
          H,                 # representative of a class
          Hzups,             # zuppos blist of <H>
          Hexts,             # extend blist of <H>
          I,                 # new subgroup found
          Ielms,             # elements of <I>
          Izups,             # zuppos blist of <I>
          N,                 # normalizer of <I>
          Nzups,             # zuppos blist of <N>
          Jzups,             # zuppos of a conjugate of <I>
          Kzups,             # zuppos of a representative in <classes>
          reps,              # transversal of <N> in <G>
          h,i,k,l,r,         # loop variables
          tom,               # table of marks (result)
          marks,             # components of the table of marks
          subs,              #
          normalizers,       #
          derivedSubgroups,  #
          groups,            #
          generators,        #
          genszups,          # mark the generators
          zupposmarks,       # mark the zuppos used
          gr, pos,           # used to computed generators for the perfect
                             # subgroups
          mrks,              # marks for one class
          ind,               # index of <I> in <N>
          D,                 # derived subgroup of <I>,
          Delms,             # elements of <D>,
          Dzups,             # zuppos blist of <D>
          DGzups,            # zuppos blist of <DG>
          order, list, perm; # used to sort the table of marks

    # compute the factorized size of <G>
    factors:=Factors(Size(G));

    # compute a system of generators for the cyclic sgr. of prime power size
    zuppos:=Zuppos(G);
    ll:=Length(zuppos);

    Info(InfoLattice,1,"<G> has ",Length(zuppos)," zuppos");

    # compute the prime corresponding to each zuppo and the index of power
    zupposPrime:=[];
    zupposPower:=[];
    for r in zuppos do
      i:=SmallestRootInt(Order(r));
      Add(zupposPrime,i);
      k:=0;
      while k <> false  do
        k:=k + 1;
        if GcdInt(i,k) = 1  then
          l:=Position(zuppos,r^(i*k));
          if l <> fail  then
            Add(zupposPower,l);
            k:=false;
          fi;
        fi;
      od;
    od;
    Info(InfoLattice,1,"powers computed");

    # get the perfect subgroups
    perfect:=RepresentativesPerfectSubgroups(G);
    perfect:=Filtered(perfect,i->Size(i)>1 and Size(i)<Size(G));

    perfectZups:=[];
    perfectNew :=[];
    for i  in [1..Length(perfect)]  do
      I:=perfect[i];
      perfectZups[i]:=BlistList(zuppos,AttributeValueNotSet(AsList,I));
      perfectNew[i]:=true;
    od;
    Info(InfoLattice,1,"<G> has ",Length(perfect),
         " representatives of perfect subgroups");


    # initialize the classes list
    nrClasses:=1;
    classesZups:=[BlistList(zuppos,[One(G)])];
    classesExts:=[DifferenceBlist(BlistList(zuppos,zuppos),classesZups[1])];
    zupposmarks:=ListWithIdenticalEntries(Length(zuppos),false);
    layere:=1;
    layerb:=1;

    # initialize the table of marks
    Info(InfoLattice,1,"computing table of marks");
    subs:=[[1]];
    marks:=[[Size(G)]];
    normalizers:=[fail];
    derivedSubgroups:=[1];
    genszups:=[[]];

    I:= DerivedSubgroup( G );
    if Size( I ) = Size( G ) then   # G perfect
        DGzups:=fail;
    elif Size(I) = 1 then       # G abelian
        DGzups:=1;
    else
        DGzups:=BlistList(zuppos,AsList(I));
    fi;
    Unbind(I);

    # loop over the layers of group (except the group itself)
    for l  in [1..Length(factors)-1]  do
      Info(InfoLattice,1,"doing layer ",l,",",
           "previous layer has ",layere-layerb+1," classes");

      # extend representatives of the classes of the previous layer
      for h  in [layerb..layere]  do

        # get the representative,its zuppos blist and extend-by blist
        H:=Subgroup( Parent(G), zuppos{genszups[h]});
        Hzups:=classesZups[h];
        Hexts:=classesExts[h];

        Info(InfoLattice,2,"extending subgroup ",h,", size = ",Size(H));

        # loop over the zuppos whose <p>-th power lies in <H>
        for i  in [1..Length(zuppos)]  do
          if Hexts[i] and Hzups[zupposPower[i]]  then

            # make the new subgroup <I>
            I:=SubgroupNC(Parent(G),Concatenation(GeneratorsOfGroup(H),
                         [zuppos[i]]));

            SetSize(I,Size(H) * zupposPrime[i]);

            # compute the zuppos blist of <I>
            Ielms:=AttributeValueNotSet(AsList,I);
            Izups:=BlistList(zuppos,Ielms);

            # compute the normalizer of <I>
            N:= Normalizer(G,I);
            ind:=Size(N) / Size(I);
            Info( InfoLattice, 2,
                  "found new class ", nrClasses + 1,
                  ", size = ", Size(I),
                  ", length = ", Size(G) / Size(N) );

            # make the new conjugacy class
            nrClasses:=nrClasses + 1;
            if l < Length(factors) -1  then
              classesZups[nrClasses]:=Izups;
            fi;
            subs[nrClasses]:=[];
            marks[nrClasses]:=[];
            genszups[nrClasses]:=ShallowCopy(genszups[h]);
            Add(genszups[nrClasses],i);
            zupposmarks[i]:=true;

            #store the extend by blist and initialize the normalizer
            if Size(N)=Size(I) then  # <I> selfnormalizing
              normalizers[nrClasses]:=nrClasses;
              if l < Length(factors)-1 then
                classesExts[nrClasses]:=
                  ListWithIdenticalEntries(ll,false);
              fi;
            elif Size(N)=Size(G) then # <I> normal
              normalizers[nrClasses]:=fail;
              if l < Length(factors) -1 then
                classesExts[nrClasses]:=
                DifferenceBlist(BlistList([1..ll],[1..ll]), Izups);
              fi;
            else
              Nzups:=BlistList(zuppos,AttributeValueNotSet(AsList,N));
              normalizers[nrClasses]:=ShallowCopy(Nzups);
              if l < Length(factors) -1 then
                SubtractBlist(Nzups,Izups);
                classesExts[nrClasses]:=Nzups;
              fi;
            fi;
            Unbind( Nzups);

            # compute the derived subgroup
            D:= AttributeValueNotSet( DerivedSubgroup, I );
            if Size(D) = Size(I) then  # <I> perfect
              derivedSubgroups[nrClasses]:=nrClasses;
            elif Size(D) = 1 then      # <I> abelian
              derivedSubgroups[nrClasses]:=1;
            else
              Delms:=AttributeValueNotSet(AsList,D);
              Dzups:=BlistList(zuppos,Delms);
            fi;
            Unbind(D);

            # compute the transversal
            reps:=RightTransversalOp(G,N);

            # set up the marking list
            mrks:=ListWithIdenticalEntries(nrClasses,0);
            mrks[nrClasses]:=1 * ind;

            # loop over the conjugates of <I>
            for r  in reps  do

              # compute the zuppos blist of the conjugate
              if r = One(G)  then
                Jzups:=Izups;
              else
                Jzups:=BlistList(zuppos,OnTuples(Ielms,r));
                if not IsBound(derivedSubgroups[nrClasses]) then
                  Dzups:=BlistList(zuppos,OnTuples(Delms,r));
                fi;
              fi;

              # look if the conjugate of <I> is the normalizer of
              # a smaller class
              for k in [2..layere] do
                if normalizers[k]=Jzups then
                  normalizers[k]:=nrClasses;
                fi;
              od;

              # look if it is the derived subgroup of G
              if IsList(DGzups) and DGzups = Jzups then
                DGzups:=nrClasses;
              fi;

              # loop over the already found classes
              for k  in [1..layere]  do
                Kzups:=classesZups[k];

                #test if the <K> is the derived subgroup of <J>
                if not IsBound(derivedSubgroups[nrClasses]) and
                   Kzups = Dzups then
                  derivedSubgroups[nrClasses]:=k;
                  Unbind(Dzups);
                  Unbind(Delms);
                fi;


                # test if the <K> is a subgroup of <J>
                if IsSubsetBlist(Jzups,Kzups)  then
                  mrks[k]:=mrks[k] + ind;
                  # don't extend <K> by the elements of <J>
                  if k >= h then
                    SubtractBlist(classesExts[k],Jzups);
                  fi;
                fi;

              od;#for k in [2..layere]

            od;#for r in reps

            # compress this line into the table of marks
            for k  in [1..nrClasses]  do
              if mrks[k] <> 0  then
                Add(subs[nrClasses],k);
                Add(marks[nrClasses],mrks[k]);
              fi;
            od;
            Info(InfoLattice,2,"testing class ",nrClasses,
                 " size = ", Size(I),
                 ", length = ",Size(G) / Size(N),", includes ",
                 Length(marks[nrClasses])," classes");

            # now we are done with the new class
            Unbind(Ielms);
            Unbind(reps);
            Unbind(I);
            Unbind(N);
            Info(InfoLattice,2,"tested inclusions");

          fi; # if Hexts[i] and Hzups[zupposPower[i]]  then ...
        od; # for i  in [1..Length(zuppos)]  do ...

        #remove the stuff we don't need anymore
        classesExts[h]:=false;
        Unbind(H);
      od; # for h  in [layerb..layere]  do ...

      # add the classes of perfect subgroups
      for i  in [1..Length(perfect)]  do
        if    perfectNew[i]
            and IsPerfectGroup(perfect[i])
            and Length(Factors(Size(perfect[i]))) = l
            then

          # make the new subgroup <I>
          I:=perfect[i];

          # compute the zuppos blist of <I>
          Ielms:=AttributeValueNotSet(AsList,I);
          Izups:=BlistList(zuppos,Ielms);

          # compute the normalizer of <I>
          N:= Normalizer(G,I);
          ind:=Size(N) / Size(I);

          Info(InfoLattice,2,"found new class ",nrClasses+1,
               ", size = ",Size(I),
               " length = ",Size(G) / Size(N));

          # make the new conjugacy class
          nrClasses:=nrClasses + 1;
          if l < Length(factors) -1 then
            classesZups[nrClasses]:=Izups;
          fi;
          subs[nrClasses]:=[];
          marks[nrClasses]:=[];
          gr:=TrivialSubgroup(G);
          genszups[nrClasses]:=[];
          k:=0;
          while Size(gr) <> Size(I) do
            k:=k+1;
            if  Izups[k] and not zuppos[k] in gr  then
              gr:=ClosureGroup(gr,zuppos[k]);
              Add(genszups[nrClasses],k);
              zupposmarks[k]:=true;
            fi;
          od;

          #store the extend by blist and initialize the normalizer
          if Size(N)=Size(I) then  # <I> selfnormalizing
            normalizers[nrClasses]:=nrClasses;
            if l < Length(factors)-1 then
              classesExts[nrClasses]:=
                ListWithIdenticalEntries(ll,false);
            fi;
          elif Size(N)=Size(G) then # <I> normal
            normalizers[nrClasses]:=fail;
            if l < Length(factors) -1 then
              classesExts[nrClasses]:=
                  DifferenceBlist(BlistList([1..ll],[1..ll]),Izups);
            fi;
          else
            Nzups:=BlistList(zuppos,AttributeValueNotSet(AsList,N));
            normalizers[nrClasses]:=ShallowCopy(Nzups);
            if l < Length(factors) -1 then
              SubtractBlist(Nzups,Izups);
              classesExts[nrClasses]:=Nzups;
            fi;
          fi;

          # compute the derived subgroup
          derivedSubgroups[nrClasses]:=nrClasses;

          # compute the transversal
          reps:=RightTransversalOp(G,N);

          # set up the marking list
          mrks:=ListWithIdenticalEntries(nrClasses,0);
          mrks[1]:=Length(reps) * ind;
          mrks[nrClasses]:=1 * ind;

          # loop over the conjugates of <I>
          for r  in reps  do

            # compute the zuppos blist of the conjugate
            if r = One(G)  then
              Jzups:=Izups;
            else
              Jzups:=BlistList(zuppos,OnTuples(Ielms,r));
            fi;

            #look if the conjugate of <I> is the normalizer of a
            #smaller class
            for k in [2..layere] do
              if normalizers[k]=Jzups then
                normalizers[k]:=nrClasses;
              fi;
            od;

            # look if it is the derived subgroup of G
            if IsList(DGzups) and DGzups = Jzups then
              DGzups:=nrClasses;
            fi;


            # loop over the perfect classes
            for k  in [i+1..Length(perfect)]  do
              Kzups:=perfectZups[k];

              # throw away classes that appear twice in perfect
              if Jzups = Kzups  then
                perfectNew[k]:=false;
                perfectZups[k]:=[];
              fi;

            od;

            # loop over all other (smaller) classes
            for k  in [2..layere]  do
              Kzups:=classesZups[k];

              # test if the <K> is a subgroup of <J>
              if IsSubsetBlist(Jzups,Kzups)  then
                mrks[k]:=mrks[k] + ind;
              fi;

            od;
          od;

          # compress this line into the table of marks
          for k  in [1..nrClasses]  do
            if mrks[k] <> 0  then
              Add(subs[nrClasses],k);
              Add(marks[nrClasses],mrks[k]);
            fi;
          od;


          Info(InfoLattice,2,"testing class ",nrClasses,", size = ",
               Size(I),
               ", length = ",Size(G) / Size(N),", includes ",
               Length(marks[nrClasses])," classes");


          # now we are done with the new class
          Unbind(Ielms);
          Unbind(reps);
          Unbind(I);
          Info(InfoLattice,2,"tested equalities");

          # unbind the stuff we dont need any more
          perfectZups[i]:=[];
        fi;
    # if IsPerfectGroup(I) and Length(Factors(Size(I))) = layer ...
      od; # for i  in [1..Length(perfect)]  do

      # on to the next layer
      layerb:=layere+1;
      layere:=nrClasses;
    od; # for l  in [1..Length(factors)-1]  do ...
    Unbind(classesZups);

    # add the whole group to the list of classes
    Info(InfoLattice,1,"doing layer ",Length(factors),",",
         " previous layer has ",layere-layerb+1," classes");
    if Size(G)>1  then
      Info(InfoLattice,2,"found whole group, size = ",Size(G),",",
                                                      "length = 1");
      nrClasses:=nrClasses + 1;
      subs[nrClasses]:=[1..nrClasses] + 0;
      marks[nrClasses]:=ListWithIdenticalEntries(nrClasses,1);
      if DGzups = fail then
        derivedSubgroups[nrClasses]:=nrClasses;
      else
        derivedSubgroups[nrClasses]:=DGzups;
      fi;
      normalizers[nrClasses]:=nrClasses;
      Info(InfoLattice,2,"testing class ",nrClasses,", size = ",
           Size(G), ", length = ",1,", includes ",
           Length(marks[nrClasses])," classes");
    fi;

    # set the normalizer for normal subgroups
    for i in [1..nrClasses-1] do
      if normalizers[i] = fail then
        normalizers[i]:=nrClasses;
      fi;
    od;

    #Sort the table of marks
    order:=List(marks,x->Size(G)/x[1]);
    list:=[1..nrClasses];
    Sort(list, function(a,b) return order[a] < order[b] or(order[a] =
          order[b] and order[normalizers[b]] <order[normalizers[a]]); end);

    perm:=Sortex(list)^-1;
    derivedSubgroups:=List(derivedSubgroups,x->x^perm);
    derivedSubgroups:=Permuted(derivedSubgroups, perm);
    normalizers:=List(normalizers, x-> x^perm);
    normalizers:=Permuted(normalizers, perm);
    subs:=List(subs,x-> List(x, y-> y^perm));
    subs:=Permuted(subs,perm);
    marks:=Permuted(marks, perm);
    for i in [1..Length(marks)] do
        SortParallel(subs[i], marks[i]);
    od;
    genszups:=Permuted(genszups, perm);

    # compute generators for each subgroup
    k:=1;
    pos:=[];
    for i in [1..Length(zuppos)] do
      if zupposmarks[i] then
        zupposmarks[i]:=k;
        k:=k+1;
        Add(pos,i);
      fi;
    od;
    generators:=Concatenation(zuppos{pos},GeneratorsOfGroup(G));
    groups:=[];
    for i in [1..nrClasses-1] do
      groups[i]:=zupposmarks{genszups[i]};
    od;
    groups[nrClasses]:=[k..k+Length(GeneratorsOfGroup(G))-1 ];

    # Make the object.
    tom:= rec( SubsTom                   := subs,
               MarksTom                  := marks,
               NormalizersTom            := normalizers,
               DerivedSubgroupsTomUnique := derivedSubgroups,
               UnderlyingGroup           := G,
               GeneratorsSubgroupsTom    := [ generators, groups ] );
    ConvertToTableOfMarks( tom );
    if HasName( G ) then
      SetIdentifier( tom, Name( G ) );
    fi;

    return tom;
end );


#############################################################################
##
#M  TableOfMarks( <mat> )  . . . . . . . . table of marks defined by a matrix
##
InstallMethod( TableOfMarks,
    "for a matrix or a lower triangular matrix",
    [ IsTable ],
    function( mat )
    local i, j, val, subs, marks, tom;

    # Check the argument.
    if not (     ForAll( mat, IsHomogeneousList )
             and ForAll( [ 1 .. Length( mat ) ],
                         i -> Length( mat[i] ) >= i ) ) then
      TryNextMethod();
    fi;

    # Setup `SubsTom' and `MarksTom' values.
    subs:= [];
    marks:= [];
    for i in [ 1 .. Length( mat ) ] do

      if   mat[i][1] <= 0 then
        Info( InfoTom, 1, "first column must have positive entries" );
        return fail;
      elif mat[i][i] = 0 then
        Info( InfoTom, 1, "diagonal entries must be nonzero" );
        return fail;
      fi;
      for j in [ i+1 .. Length( mat[i] ) ] do
        if mat[i][j] <> 0 then
          Info( InfoTom, 1, "the matrix must be lower triangular" );
          return fail;
        fi;
      od;

      subs[i]:= [];
      marks[i]:= [];

      for j in [ 1 .. i ] do
        val:= mat[i][j];
        if   val < 0 then
          Info( InfoTom, 1, "all entries must be nonnegative integers" );
          return fail;
        elif 0 < val then
          Add( subs[i], j );
          Add( marks[i], mat[i][j] );
        fi;
      od;

    od;

    # Make the object.
    tom:= rec( SubsTom  := subs,
               MarksTom := marks );
    ConvertToTableOfMarks( tom );

    # Test it.
    if not IsInternallyConsistent( tom ) then
      return fail;
    fi;

    # Return it.
    return tom;
    end );


#############################################################################
##
#F  TableOfMarksFromLibrary( <name> )
##
##  The `TableOfMarks' method for a string calls `TableOfMarksFromLibrary'.
##  If the library of tables of marks is not available then we bind this
##  to a dummy function that signals an error.
##
if not IsBoundGlobal( "TableOfMarksFromLibrary" ) then
  BindGlobal( "TableOfMarksFromLibrary", function( arg )
      Error( "sorry, the GAP Tables Of Marks Library is not installed" );
      end );
fi;


#############################################################################
##
#M  TableOfMarks( <name> )  . . . . . . . . . . library table with given name
##
InstallMethod( TableOfMarks,
    "for a string (dispatch to `TableOfMarksFromLibrary')",
    [ IsString ],
    str -> TableOfMarksFromLibrary( str ) );


#############################################################################
##
#M  LatticeSubroups( <G> )
##
##  method for a group with table of marks
##  method for a cyclic group
##
##  LatticeSubgroupsByTom( <G> )
##
InstallGlobalFunction( LatticeSubgroupsByTom, function( G )
    local marks, i, lattice, classes, tom;

    # Get the classes.
    tom:= TableOfMarks( G );
    classes:= List( [1..Length(OrdersTom( tom))], x-> ConjugacyClassSubgroups
                      (G, RepresentativeTom( tom, x)));

    marks:=MarksTom(tom);
    for i in [1..Length(classes)] do
         SetSize(classes[i],marks[i][1]/Last(marks[i]));
    od;

    # Create the lattice.
    lattice:=Objectify(NewType(FamilyObj(classes),IsLatticeSubgroupsRep),
                       rec());
    lattice!.conjugacyClassesSubgroups:=classes;
    lattice!.group     :=G;

    # Return the lattice.
    return lattice;
    end );

InstallMethod( LatticeSubgroups,
    "for a group with table of marks",
    [ IsGroup and HasTableOfMarks ], 10,
    LatticeSubgroupsByTom );

InstallMethod( LatticeSubgroups,
    "for a cyclic group",
    [ IsGroup and IsCyclic ],
    LatticeSubgroupsByTom );


#############################################################################
##
##  5. Printing Tables of Marks
##


#############################################################################
##
#M  ViewObj( <tom> ) . . . . . . . . . . . . . . . . . print a table of marks
##
InstallMethod( ViewObj,
    [ IsTableOfMarks ],
    function( tom )
    Print( "TableOfMarks( " );
    if   HasIdentifier( tom ) then
      Print( "\"", Identifier( tom ), "\"" );
    elif HasUnderlyingGroup( tom ) then
      ViewObj( UnderlyingGroup( tom ) );
    elif HasMarksTom( tom ) then
      Print( "<", Length( MarksTom( tom ) ), " classes>" );
    else
      Print( "<nothing useful known>" );
    fi;
    Print( " )" );
    end );


#############################################################################
##
#M  PrintObj( <tom> )
##
InstallMethod( PrintObj,
    [ IsTableOfMarks ],
    function( tom )
    Print( "TableOfMarks( " );
    if   HasIdentifier( tom ) then
      Print( "\"", Identifier( tom ), "\"" );
    elif HasUnderlyingGroup( tom ) then
      PrintObj( UnderlyingGroup( tom ) );
    elif HasMarksTom( tom ) then
      Print( "<", Length( MarksTom( tom ) ), " classes>" );
    else
      Print( "<nothing useful known>" );
    fi;
    Print( " )" );
    end );


#############################################################################
##
#M  Display( <tom>[, <options>] )  . . . . . . . . . display a table of marks
##
InstallMethod( Display,
    "for a table of marks (add empty options record)",
    [ IsTableOfMarks ],
    function( tom )
    Display( tom, rec() );
    end );

InstallOtherMethod( Display,
    "for a table of marks and an options record",
    [ IsTableOfMarks, IsRecord ],
    function( tom, options )
    local i, j, k, l, pr1, ll, lk, von, bis, pos, llength, pr, vals, subs,
          classes, lc, ci, wt;

    #  default values.
    subs:= SubsTom(tom);
    ll:= Length(subs);
    classes:= [1..ll];
    vals:= MarksTom(tom);

    #  adjust parameters.
    if IsBound(options.classes) and IsList(options.classes) then
      classes:= options.classes;
    fi;
    if IsBound(options.form) then
      if options.form = "supergroups" then
        vals:= ShallowCopy(vals);
        wt:= WeightsTom(tom);
        for i in [1..ll] do
          vals[i]:= vals[i]/wt[i];
        od;
      elif options.form = "subgroups" then
        vals:= NrSubsTom(tom);
      fi;
    fi;

    llength:= SizeScreen()[1];
    von:= 1;
    pr1:= LogInt(ll, 10);

    #  determine column width.
    pr:= List([1..ll], x->0);
    for i in [1..ll] do
      for j in [1..Length(subs[i])] do
        pr[subs[i][j]]:= Maximum(pr[subs[i][j]], LogInt(vals[i][j], 10));
      od;
    od;

    lc:= Length(classes);
    while von <= lc do
      bis:= von;

      #  how many columns on this page?
      lk:= pr1 + 5 + pr[classes[von]];
      while bis < lc and lk+2+pr[classes[bis+1]] <= llength do
        bis:= bis+1;
        lk:= lk+2+pr[classes[bis]];
      od;

      #  loop over rows.
      for i in [von..lc] do
        ci:= classes[i];
        for k in [1 .. pr1-LogInt(ci, 10)] do
          Print(" ");
        od;
        Print(ci, ": ");

        #  loop over columns.
        for j in [von .. Minimum(i, bis)] do
          pos:= Position(subs[ci], classes[j]);
          if pos <> fail and pos > 0 then
            l:= LogInt(vals[ci][pos], 10)-1;
          else
            l:= -1;
          fi;
          for k in [1 .. pr[classes[j]] - l] do
            Print(" ");
          od;
          if pos = fail then
            Print(".\c");
          else
            Print(vals[ci][pos], "\c");
          fi;
        od;
        Print("\n");
      od;

      von:= bis+1;
      Print("\n");
    od;
    end );


#############################################################################
##
##  6. Sorting Tables of Marks
##


#############################################################################
##
#M  SortedTom( <tom>, <perm> )  . . . . . . . . . . . . sorted table of marks
##
InstallMethod( SortedTom,
    [ IsTableOfMarks, IsPerm ],
    function( tom, perm )
    local i, components;

    components:= rec();

    if HasIdentifier( tom ) then
      components.Identifier:= Identifier( tom );
    fi;
    components.SubsTom:= Permuted( List( SubsTom( tom ),
                                   x -> ShallowCopy( OnTuples( x, perm ) ) ),
                                   perm);
    components.MarksTom:= Permuted( List( MarksTom( tom ), ShallowCopy ),
                                    perm );
    for i in [ 1 .. Length( components.SubsTom ) ] do
      SortParallel( components.SubsTom[i], components.MarksTom[i] );
    od;
    if HasNormalizersTom( tom ) then
      components.NormalizersTom:=
          Permuted( OnTuples( NormalizersTom( tom ), perm ), perm );
    fi;
    if HasDerivedSubgroupsTomUnique( tom ) then
       components.DerivedSubgroupsTomUnique:=
           Permuted( OnTuples( DerivedSubgroupsTomUnique( tom ), perm ),
                     perm );
    fi;
    if HasUnderlyingGroup( tom ) then
      components.UnderlyingGroup:= UnderlyingGroup( tom );
    fi;
    if HasStraightLineProgramsTom( tom ) then
      components.StraightLineProgramsTom:=
          Permuted( StraightLineProgramsTom( tom ), perm );
    fi;
    if HasGeneratorsSubgroupsTom(tom) then
      components.GeneratorsSubgroupsTom:=
          [ GeneratorsSubgroupsTom( tom )[1],
            Permuted( GeneratorsSubgroupsTom( tom )[2], perm ) ];
    fi;

    ConvertToTableOfMarks( components );

    if HasPermutationTom( tom ) then
      SetPermutationTom( components, PermutationTom( tom ) * perm );
    else
      SetPermutationTom( components, perm );
    fi;

    return components;
    end );


#############################################################################
##
##  7. Technical Details about Tables of Marks
##


#############################################################################
##
#F  ConvertToTableOfMarks( <record> )
##
InstallGlobalFunction( ConvertToTableOfMarks, function( record )
    local i, names;
    names:= RecNames( record );

    # Make the object.
    Objectify( NewType( TableOfMarksFamily,
                        IsTableOfMarks and IsAttributeStoringRep ),
               record );

    # Set the attributes values.
    for i in [ 1, 3 .. Length( TableOfMarksComponents )-1 ] do
      if TableOfMarksComponents[i] in names then
        Setter( TableOfMarksComponents[i+1] )( record,
                record!.( TableOfMarksComponents[i] ) );
      fi;
    od;

    return record;
    end );


#############################################################################
##
##  8. Attributes of Tables of Marks
##


#############################################################################
##
#M  MarksTom( <tom> ) . . . . . . . . . . . . . . . . . . . . . . . the marks
##
InstallMethod( MarksTom,
    "for a table of marks with known `NrSubsTom' and `OrdersTom'",
    [ IsTableOfMarks and HasNrSubsTom and HasOrdersTom ],
    function( tom )
    local i, j, ll, order, length, nrSubs, subs, marks, ord;

    # get the attributes and initialize
    order:=OrdersTom(tom);
    subs:=SubsTom(tom);
    length:=LengthsTom(tom);
    nrSubs:=NrSubsTom(tom);
    ll:=Length(order);
    ord:=order[ll];
    marks:=[[ord]];

    # Compute the marks.
    for i in [ 2 .. ll ] do
      marks[i]:= [ ord / order[i] ];
      for j in [ 2 .. Length( subs[i] ) ] do
        marks[i][j]:= nrSubs[i][j] * marks[i][1] / length[ subs[i][j] ];
        if not IsInt( marks[i][j] ) or marks[i][j] < 0 then
          Info( InfoTom, 1,
                "orbit length ", i, ", ", j, ": ", marks[i][j] );
        fi;
      od;
    od;

    return marks;
    end );


#############################################################################
##
#M  NrSubsTom( <tom> ) . . . . . . . . . . . . . . . . . numbers of subgroups
##
InstallMethod( NrSubsTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    function( tom )
    local i, j, nrSubs, subs, marks, length, index;

    # initialize
    length:= [];
    nrSubs:= [];
    subs:= SubsTom( tom );
    marks:= MarksTom( tom );

    # compute the numbers row by row
    for i in [ 1 .. Length( subs ) ] do
      index:= marks[i][Position(subs[i], 1)];
      length[i]:= index / marks[i][Position(subs[i], i)];
      nrSubs[i]:= [];

      for j in [1..Length(subs[i])] do
        nrSubs[i][j]:= marks[i][j] * length[subs[i][j]] / index;
        if not IsInt( nrSubs[i][j] ) or nrSubs[i][j] < 0 then
          Info( InfoTom, 1,
                "orbit length ", i, ", ", j, ": ", nrSubs[i][j] );
        fi;
      od;

    od;

    return nrSubs;
    end );


#############################################################################
##
#M  OrdersTom( <tom> )  . . . . . . . . . . . . . . . . . orders of subgroups
##
InstallMethod( OrdersTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    function( tom )
    local subs, marks;
    subs:= SubsTom( tom );
    marks:= MarksTom( tom );
    return List( [ 1 .. Length( subs ) ],
                 i -> marks[1][1] / marks[i][ Position( subs[i], 1 ) ] );
    end );


#############################################################################
##
#M  LengthsTom( <tom> )  . . . . . . . . . .  length of the conjugacy classes
##
InstallMethod( LengthsTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    function( tom )
    local nrSubs;
    nrSubs:= NrSubsTom( tom );
    return Last(nrSubs);
    end );


#############################################################################
##
#M  ClassTypesTom( <tom> )  . . . . . . . . . . . . . . .  types of subgroups
##
InstallMethod( ClassTypesTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    function( tom )
    local i, j, nrsubs, subs, order, type, struct, nrtypes;

    nrsubs:= NrSubsTom(tom);
    subs:= SubsTom(tom);
    order:=OrdersTom(tom);
    type:= [];
    struct:= [];
    nrtypes:= 1;

    for i in [1..Length(subs)] do

      # determine type
      # classify according to the number of subgroups
      struct[i]:= [];
      for j in [2..Length(subs[i])-1] do
        if IsBound(struct[i][type[subs[i][j]]]) then
          struct[i][type[subs[i][j]]]:=
              struct[i][type[subs[i][j]]] + nrsubs[i][j];
        else
          struct[i][type[subs[i][j]]]:= nrsubs[i][j];
        fi;
      od;

      # consider the order
      for j in [1..i-1] do
        if order[j] = order[i] and struct[j] = struct[i] then
          type[i]:= type[j];
        fi;
      od;

      if not IsBound(type[i]) then
        type[i]:= nrtypes;
        nrtypes:= nrtypes+1;
      fi;
    od;

    return type;
    end );


#############################################################################
##
#F  ClassNamesTom( <tom> )  . . . . . . . . . . . . . . . . . . . class names
##
InstallMethod( ClassNamesTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    function( tom )
    local i, c, classes, type, name, count, ord, alp, la;

    type:= ClassTypesTom(tom);

    #  form classes.
    classes:= List([1..Maximum(type)], x-> rec(elts:= []));
    for i in [1..Length(type)] do
        Add(classes[type[i]].elts, i);
    od;

    #  determine type.
    count:= rec();
    for i in [1..Length(classes)] do
        ord:= String(OrdersTom(tom)[classes[i].elts[1]]);
        if IsBound(count.(ord)) then
            count.(ord).nr:= count.(ord).nr + 1;
            if count.(ord).nr < 10 then
                classes[i].type:=
                  Concatenation("_", String(count.(ord).nr));
            else
                classes[i].type:=
                  Concatenation("_{", String(count.(ord).nr), "}");
            fi;
        else
            count.(ord):= rec(first:= classes[i], nr:= 1);
            classes[i].type:= "_1";
        fi;

        #  cyclic?
        if Set(NrSubsTom(tom)[classes[i].elts[1]]) = [1]
           and IsCyclicTom(tom, classes[i].elts[1]) then
            classes[i].order:= ord;
            classes[i].type:= "";
        else
            classes[i].order:= Concatenation("(", ord, ")");
        fi;

    od;

    #  omit unique types.
    for i in RecNames(count) do
        if count.(i).nr = 1 then
            count.(i).first.type:= "";
        fi;
    od;

    #  construct names.
    name:= [];
    alp:= ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
           "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
    la:= Length(alp);
    for c in classes do
        if Length(c.elts) = 1 then
            name[c.elts[1]]:= Concatenation(c.order, c.type);
        else
            for i in [1..Length(c.elts)] do
                if i <= la then
                    name[c.elts[i]]:= Concatenation(c.order,c.type,alp[i]);
                elif i <= la * (la+1) then
                    name[c.elts[i]]:= Concatenation(c.order, c.type,
                           alp[QuoInt(i-1, la)], alp[((i-1) mod la) +1]);

                else
                    Error("did not expect more than ", la * (la+1),
                          "classes of the same type");

                fi;
            od;
        fi;
    od;

    for c in name do
      ConvertToStringRep( c );
    od;

    return name;
    end );


#############################################################################
##
#M  FusionsTom( <tom> )
##
InstallMethod( FusionsTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    x -> [] );


#############################################################################
##
#M  IdempotentsTom( <tom> ) . . . . . . . . . . . . . . . . . . . idempotents
##
InstallMethod( IdempotentsTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    function( tom )
    local i, c, classes, p, ext, marks;

    marks:= MarksTom( tom );
    classes:= [ 1 .. Length( marks ) ];

    for p in PrimeDivisors( marks[1][1] ) do
      ext:= CyclicExtensionsTom( tom, p );
      for c in ext do
        for i in c do
          classes[i]:= classes[ c[1] ];
        od;
      od;
    od;

    for i in [ 1 .. Length( classes ) ] do
      classes[i]:= classes[ classes[i] ];
    od;

    return classes;
    end );


#############################################################################
##
#M  IdempotentsTomInfo( <tom> ) . . . . . . . . . . . . . . . . . idempotents
##
InstallMethod( IdempotentsTomInfo,
    "for a table of marks",
    [ IsTableOfMarks ],
    function( tom )
    local ext, ll, result, class, idem;

    ext:= CyclicExtensionsTom( tom );
    ll:= Length( SubsTom( tom ) );
    result:= rec( primidems       := [],
                  fixpointvectors := [] );

    for class in ext do

      idem:= ListWithIdenticalEntries( ll, 0 );
      idem{ class }:= List( class, x -> 1 );
      Add( result.fixpointvectors, idem );
      Add( result.primidems, DecomposedFixedPointVector( tom, idem ) );

    od;

    return result;
    end );


#############################################################################
##
#M  MatTom( <tom> ) . . . . . . convert compressed table of marks into matrix
##
InstallMethod( MatTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    function( tom )
    local i, j, subs, marks, ll, res;

    marks:= MarksTom( tom );
    subs:= SubsTom( tom );
    ll:= [ 1 .. Length( subs ) ];

    res:= [];
    for i in ll do
      res[i]:= ListWithIdenticalEntries( Length( ll ), 0 );
      for j in [ 1 .. Length( subs[i] ) ] do
        res[i][ subs[i][j] ]:= marks[i][j];
      od;
    od;

    return res;
    end );


#############################################################################
##
#M  MoebiusTom( <tom> ) . . . . . . . . . . . . . . . . . .  Moebius function
##
InstallMethod( MoebiusTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    function( tom )
    local i, j, mline, nline, ll, mdec, ndec, expec, done, no, comsec,
          order, subs, nrsubs, length, der, result;

    nrsubs:= NrSubsTom(tom);
    subs:= SubsTom(tom);
    length:= LengthsTom(tom);
    order:=OrdersTom(tom);
    mline:= List(subs, x-> 0);
    nline:= List(subs, x-> 0);
    ll:= Length( subs );
    mline[ll]:= 1;
    nline[ll]:= 1;

    # decompose mline with tom
    # decompose nline w.r.t. incidence
    mdec:= [];
    done:= false;
    i:= Length(mline);
    while not done do
      while i>0 and mline[i] = 0 do
        i:= i-1;
      od;
      if i = 0 then
        done:= true;
      else
        mdec[i]:= mline[i];
        for j in [1..Length(subs[i])] do
          mline[subs[i][j]]:= mline[subs[i][j]] - mdec[i]*nrsubs[i][j];
        od;
        mdec[i]:= mdec[i] / length[i];
      fi;
    od;

    ndec:= [];
    done:= false;
    i:= Length(nline);
    while not done do
      while i>0 and nline[i] = 0 do
        i:= i-1;
      od;
      if i = 0 then
        done:= true;
      else
        ndec[i]:= nline[i];
        for j in subs[i] do
          nline[j]:= nline[j] - ndec[i];
        od;
      fi;
    od;

    result:= rec( mu := mdec,
                  nu := ndec );

    # Determine intersections with the derived subgroup of the whole group
    # if this can be uniquely determined.
    der:= DerivedSubgroupTom( tom, ll );
    if IsInt( der ) then

      expec:= [];
      if der <> ll then
        comsec:= [];
        for i in [ 1 .. ll ] do

          # There is only one intersection with normal subgroups.
          comsec[i]:= Number( IntersectionsTom( tom, i, der ), x -> x <> 0 );

        od;
        for i in [ 1 .. Length( ndec ) ] do
          if IsBound( ndec[i] ) then
            no:= NormalizersTom( tom )[i];

            #  maybe the normalizer is not unique.
            if IsList( no ) then
              no:= List( no, x -> order[ comsec[x] ] );
              no:= Set( no );
              if Size( no ) > 1 then
                Info( InfoTom, 2,
                      "Size of normalizer ", i, " not unique." );
              else
                no:= no[1];
              fi;
            else
              no:= order[ comsec[ no ] ];
            fi;
            expec[i]:= ndec[i] * no / order[ comsec[i] ];
          fi;
        od;

      else

        # The group is perfect.
        for i in [ 1 .. Length( ndec ) ] do
          if IsBound( ndec[i] ) then
            expec[i]:= ndec[i] * order[ ll ] / order[i] / length[i];
          fi;
        od;

      fi;

      result.ex:= expec;
      result.hyp:= Filtered( [ 1 .. Length( expec ) ],
                             function( x )
                               if IsBound( expec[x] ) then
                                 return    ( not IsBound( mdec[x] ) )
                                        or expec[x] <> mdec[x];
                               else
                                 return IsBound( mdec[x] );
                               fi;
                             end );

    fi;

    return result;
    end );


#############################################################################
##
#M  WeightsTom( <tom> ) . . . . . . . . . . . . . . . . . . . . . . . weights
##
InstallMethod( WeightsTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    function( tom )
    local subs, marks;

    marks:= MarksTom(tom);
    subs:= SubsTom(tom);

    return List( [ 1 .. Length( subs ) ],
                 i -> marks[i][ Position( subs[i], i ) ] );
    end );


#############################################################################
##
##  9. Properties of Tables of Marks
##


#############################################################################
##
#M  IsAbelianTom( <tom>[, <sub>] )
##
##  If the group of <tom> is known then `IsAbelianTom' delegates the task
##  to the group.
##  Otherwise it is used that a group is abelian if and only if all subgroups
##  are normal and the group contains no quaternion group of order $8$.
##
InstallMethod( IsAbelianTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    function( tom )
    local marks, subs, nrSubs, order, result, sub, number, sub1;

    result:=true;
    marks:=MarksTom(tom);
    order:=OrdersTom(tom);
    subs:=SubsTom(tom);
    nrSubs:=NrSubsTom(tom);

    # All subgroups must be normal.
    for sub in [ 1 .. Length( order ) ] do
      if marks[ sub ][1] <> Last(marks[ sub ]) then
        return false;
      fi;
    od;

    # Test the subgroups of order $8$.
    for sub in [2..Length(order)] do
      if order[sub]=8 then
        #count the number of subgroups of sub
        number:=0;
        for sub1 in subs[sub] do
          number:=number+nrSubs[sub][Position(subs[sub],sub1)];
        od;
        #q8 is determined by its number of subgroups
        if number=6 then
          return false;
        fi;
      fi;
    od;

    return result;
    end );

InstallMethod( IsAbelianTom,
    "for a table of marks and a positive integer",
    [ IsTableOfMarks, IsPosInt ], 10,
    function( tom, sub )
    sub:= DerivedSubgroupTom( tom, sub );
    if IsInt( sub ) then
      return sub = 1;
    elif not 1 in sub then
      return false;
    else
      TryNextMethod();
    fi;
    end );

InstallMethod( IsAbelianTom,
    "for a table of marks with known der. subgroups, and a positive integer",
    [ IsTableOfMarks and HasDerivedSubgroupsTomUnique, IsPosInt ], 1000,
    function( tom, sub )
    return DerivedSubgroupsTomUnique( tom )[ sub ] = 1;
    end );

InstallMethod( IsAbelianTom,
    "for a table of marks with generators, and a positive integer",
    [ IsTableOfMarks and IsTableOfMarksWithGens, IsPosInt ],
    function( tom, sub )
    return IsAbelian( RepresentativeTom( tom, sub ) );
    end );


#############################################################################
##
#M  IsCyclicTom( <tom>[, <sub>] ) . . . .  check whether a subgroup is cyclic
##
##  A subgroup is cyclic if and only if the sum of the corresponding row of
##  the inverse table of marks is nonzero (see Kerber, S. 125).
##  Thus we only have to decompose the corresponding idempotent.
##
InstallMethod( IsCyclicTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    tom -> IsCyclicTom( tom, Length( SubsTom( tom ) ) ) );

InstallMethod( IsCyclicTom,
    "for a table of marks and a positive integer",
    [ IsTableOfMarks, IsPosInt ],
    function( tom, sub )
    local mline;

    mline:= 0 * [ 1 .. sub ];
    mline[ sub ]:= 1;

    # Decompose mline w.r.t. tom, and determine whether the sum is nonzero.
    return Sum( DecomposedFixedPointVector( tom, mline ), 0 ) <> 0;
    end );


#############################################################################
##
#M  IsNilpotentTom( <tom>[, <sub>] )
##
InstallMethod( IsNilpotentTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    tom -> IsNilpotentTom( tom, Length( SubsTom( tom ) ) ) );

InstallMethod( IsNilpotentTom,
    "for a table of marks and a positive integer",
    [ IsTableOfMarks, IsPosInt ],
    function( tom, sub )
    local  factors, primes, exponents, i, pos;

    factors:=Factors(OrdersTom(tom)[sub]);
    factors:=Collected(factors);
    primes:=List(factors,x->x[1]);
    exponents:=List(factors,x->x[2]);
    for i in [1..Length(primes)] do
      pos:= Position( OrdersTom( tom ){ SubsTom( tom )[ sub ] },
                      primes[i]^exponents[i] );
      if ContainedTom(tom,SubsTom(tom)[sub][pos],sub) > 1 then
        return false;
      fi;
    od;
    return true;
    end );


#############################################################################
##
#M  IsPerfectTom( <tom>[, <sub>] )
##
##  A finite group is perfect if and only if it has no normal subgroup of
##  prime index.
##  This is tested here.
##
##  If <tom> knows its underlying group the task is delegated to th group.
##
InstallMethod( IsPerfectTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    tom -> IsPerfectTom( tom, Length( SubsTom( tom ) ) ) );

InstallMethod( IsPerfectTom,
    "for a table of marks with known der. subgroups, and a positive integer",
    [ IsTableOfMarks and HasDerivedSubgroupsTomUnique, IsPosInt ],
    function( tom, sub )
    return DerivedSubgroupsTomUnique( tom )[ sub ] = sub;
    end );

InstallMethod( IsPerfectTom,
    "for a table of marks and a positive integer",
    [ IsTableOfMarks, IsPosInt ],
    function( tom, sub )
    local ext, pos;
    ext:=CyclicExtensionsTom(tom);
    pos:=PositionProperty(ext,x-> sub in x);
    return sub = Minimum(ext[pos]);
    end );


#############################################################################
##
#M  IsSolvableTom( <tom>[, <sub>] )
##
InstallMethod( IsSolvableTom,
    "for a table of marks",
    [ IsTableOfMarks ],
    tom -> IsSolvableTom( tom, Length( SubsTom( tom ) ) ) );

InstallMethod( IsSolvableTom,
    "for a table of marks and a positive integer",
    [ IsTableOfMarks, IsPosInt ],
    function( tom, sub )
    local ext, pos;

    ext:= CyclicExtensionsTom( tom );
    pos:= PositionProperty( ext, x -> 1 in x );

    return sub in ext[ pos ];
    end );


#############################################################################
##
##  10. Other Operations for Tables of Marks
##


#############################################################################
##
#M  IsInternallyConsistent( <tom> ) . .  consistency check for table of marks
##
##  The tensor product of two rows of the table of marks decomposes into
##  rows of the table of marks with integer coefficients.
##
BindGlobal( "TestRow", function( tom, n )
    local i, j, k, a, b, dec, test, marks, subs;

    test:= true;
    marks:= MarksTom(tom);
    subs:= SubsTom(tom);


    a:= [];

    # decompress the nth line of <tom>
    for i in [1..Length(subs[n])] do
      a[subs[n][i]]:= marks[n][i];
    od;


    for i in Reversed([1..n]) do
      # build the tensor product with row <i>
      b:= [];
      for j in [1..Length(subs[i])] do
        k:= subs[i][j];
        if IsBound(a[k]) then
          b[k]:= a[k]*marks[i][j];
        fi;
      od;
      for j in [1..Length(b)] do
        if not IsBound(b[j]) then
          b[j]:= 0;
        fi;
      od;

      # deompose and test the tensor product
      dec:= DecomposedFixedPointVector(tom, b);
      if ForAny(Set(dec), x-> not IsInt(x) or (x < 0)) then
        Info(InfoTom,2, n, ".", i, " = ", dec);
        test:= false;
      fi;
    od;

    return test;
end );

InstallMethod( IsInternallyConsistent,
    "for a table of marks, decomposition test",
    [ IsTableOfMarks ],
    function( tom )
    local test, g, i;

    test:= true;

    # Check that the underlying group has the right order.
    if HasUnderlyingGroup( tom ) then
      g:= UnderlyingGroup( tom );
      if Size( g ) <> Size( Group( GeneratorsOfGroup( g ), One( g ) ) ) then
        return false;
      fi;
    fi;

    for i in [ 1 .. Length( SubsTom( tom ) ) ] do
      if not TestRow( tom, i ) then
        return false;
      fi;
    od;

    return test;
    end );


#############################################################################
##
#M  DerivedSubgroupTom( <tom>, <sub> )
##
InstallMethod( DerivedSubgroupTom,
    "for a table of marks, and a positive integer",
    [ IsTableOfMarks, IsPosInt ],
    function( tom, sub )
    local set, primes, normalsubs, minindex, p, nrsubs, ext, pos, extp,
          extps, sub1, sub2, result, i, j, indexsub1, indexsub2, index, int,
          notnormal, res, factorel, norm, oddord,
          normext, bool, n, orders, subs, isnormal, grd, der, poss;

    # Check whether the derived subgroup has been computed already.
    if HasDerivedSubgroupsTomUnique( tom ) then
      return DerivedSubgroupsTomUnique( tom )[ sub ];
    fi;

    # Perhaps this is not the first time one has asked for this value.
    poss:= DerivedSubgroupsTomPossible( tom );
    if IsBound( poss[ sub ] ) then
      return poss[ sub ];
    fi;

    # First consider the trivial cases.
    if IsCyclicTom( tom, sub ) then
      result:= 1;
    elif IsPerfectTom( tom, sub ) then
      result:= sub;
    else

      # Compute the possibilities.
      isnormal:=function(tom,sub1,sub2)
          local sub, result;
          result:=false;
          if ContainedTom(tom,sub1,sub2)=1 then
              result:=true;
          else
              if IsInt(NormalizersTom(tom)[sub1]) then
                  if NormalizersTom(tom)[sub1]=sub2   then
                      result:=true;
                  elif sub2 in subs[NormalizersTom(tom)[sub1]] then
                      result:=0;
                  fi;
              else
                  for sub in NormalizersTom(tom)[sub1] do
                      if sub2 in subs[sub] then
                          result:=0;
                      fi;
                  od;
              fi;
          fi;
          return result;
      end;

      orders:=OrdersTom(tom);
      subs:=SubsTom(tom);

      # find normal subgroups of prime index
      set:=PrimeDivisors(orders[sub]);
      primes:=[];
      normalsubs:=[];
      minindex:=1;
      for p in set do
          nrsubs:=0;
          ext:=CyclicExtensionsTom(tom,p);
          pos:=PositionProperty(ext,x->sub in x);
          extp:=Filtered(ext[pos],x->x in subs[sub] and orders[x] =
                        orders[sub]/p);

          extps:=Filtered(ext[pos],x-> x in subs[sub] and orders[x]
                         = orders[sub]/p^2);
          extps:=Filtered(extps,x->isnormal(tom,x,sub) = true);
          Append(normalsubs,extps);
          for sub1 in extp do
              nrsubs:=nrsubs + ContainedTom(tom,sub1,sub);
              Add(primes,p);
              if Length(Intersection(subs[sub1],extps)) = 0 then
                  Add(normalsubs,sub1);
              fi;
          od;
          if nrsubs <> 0 then
              nrsubs:=Length(Factors(nrsubs*(p-1)+1));
              minindex:=minindex*p^nrsubs;
          fi;
      od;
      primes:=Set(primes);

      # compute subgroups of sub which are connected by a chain of normal
      # extensions or order in primes
      ext:=CyclicExtensionsTom(tom,primes);
      ext:=ext[PositionProperty(ext,x-> sub in x)];

      # consider intersections of two normal subgroups
      # for each such intersection the derived subgroup must be
      # contained in one of the possible intersections returned by
      # `IntersectionsTom'.
      # Additionally there must be a chain of
      # normal extensions connecting the derived subgroup and the groupext;
      result:=Filtered(subs[normalsubs[1]], x-> x in ext);
      for i in [1..Length(normalsubs)] do
          sub1:=normalsubs[i];
          indexsub1:=orders[sub]/orders[sub1];
          for j in [i..Length(normalsubs)] do
              sub2:=normalsubs[j];
              if sub1<>sub2 or(ContainedTom(tom,sub1,sub)<>1 and
                         IsPrime(indexsub1)) then
                  indexsub2:=orders[sub]/orders[sub2];
                  index:=[indexsub1*indexsub2];
                  if not (IsPrime(indexsub1) or IsPrime(indexsub2) or
                          indexsub1<>
                          indexsub2) then
                      Add(index,Factors(indexsub1)[1]^3);
                  fi;
                  int:=IntersectionsTom(tom,sub1,sub2);
                  int:= Filtered( [ 1 .. Length( int ) ], x -> int[x] <> 0 );
                  int:=Filtered(int,x->orders[sub]/orders[x] in index);
                  int:=Filtered(int,x-> x in ext);
                  int:=List(int,x->subs[x]);

                  int:=Flat(int);
                  int:=Filtered(int,x-> x in ext);
                  result:=Intersection(result,int);
              fi;
          od;
      od;

      if IsTableOfMarksWithGens(tom) then
          # correct size is known
          der:=DerivedSubgroup(RepresentativeTom(tom,sub));
          result:=Filtered(result,x->orders[x]  = Size(der));

      else
          # forget all collected subgroups whose index is too small
          result:=Filtered(result,x->(orders[sub]/orders[x])
                          >=minindex);
      fi;

      # the derived subgroup must be normal
      notnormal:=Filtered(subs[sub],x-> isnormal(tom,x,sub)=false);
      result:=Difference(result,notnormal);

      # sub cannot be abelian if it contains a not-normal subgroup
      if IntersectionSet( notnormal, subs[ sub ] ) <> [] then
        RemoveSet( result, 1 );
      fi;

      if Length( result ) = 1 then
        result:= result[1];
      else

        # the factor group cannot contain a not normal member
        # if the factor group for one possible solution is cyclic
        # it must contain the derived subgroup
        res:=[];
        for sub1 in Filtered(result,x->ContainedTom(tom,x,sub) = 1) do
            #inspecting the factor group if possible
            #collect the elements of the factor group that are not normal
            factorel:=Filtered(subs[sub], x->sub1 in subs[x]
                              and x in notnormal);

            if Length(factorel) >0 then
                Add(res,sub1);
            fi;
        od;
        result:=Difference(result,res);

        if Length( result ) = 1 then
          result:= result[1];
        else

          # the derived subgroup must be normal in every normal extension of sub
          # and the derived subgroup can't be an involution if any normal
          # extension of sub has a cyclic subgroup of odd order 'n' and no
          # cyclic subgroup of order '2*n'
          norm:=NormalizersTom(tom)[sub];
          if IsInt(norm) then
              normext:=Filtered(subs[norm],x->sub in subs[x] and
                               isnormal(tom,sub,x)=true);
              res:=Filtered(result,
                           x->ForAny(normext, y->isnormal(tom,x,y) = false));
              result:=Difference(result,res);
              if 2 in orders{result} then
                  bool:=true;
                  for sub1 in normext do
                      res:=Filtered(subs[sub1],x->IsCyclicTom(tom,x));
                      oddord:=2*Filtered(orders{res},IsOddInt);

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

--> maximum size reached

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

[ zur Elbe Produktseite wechseln0.68Quellennavigators  Analyse erneut starten  ]