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


SSL ctbl.gi   Interaktion und
Portierbarkeitunbekannt

 
#############################################################################
##
##  This file is part of GAP, a system for computational discrete algebra.
##  This file's authors include Thomas Breuer, Götz Pfeiffer.
##
##  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 the implementations corresponding to the declarations
##  in `ctbl.gd'.
##
##  1. Some Remarks about Character Theory in GAP
##  2. Character Table Categories
##  3. The Interface between Character Tables and Groups
##  4. Operators for Character Tables
##  5. Attributes and Properties for Groups as well as for Character Tables
##  6. Attributes and Properties only for Character Tables
##  x. Operations Concerning Blocks
##  7. Other Operations for Character Tables
##  8. Creating Character Tables
##  9. Printing Character Tables
##  10. Constructing Character Tables from Others
##  11. Sorted Character Tables
##  12. Storing Normal Subgroup Information
##  13. Auxiliary Stuff
##


#############################################################################
##
##  1. Some Remarks about Character Theory in GAP
##


#############################################################################
##
##  2. Character Table Categories
##


#############################################################################
##
##  3. The Interface between Character Tables and Groups
##


#############################################################################
##
#F  CharacterTableWithStoredGroup( <G>, <tbl>[, <arec>] )
#F  CharacterTableWithStoredGroup( <G>, <tbl>, <bijection> )
##
InstallGlobalFunction( CharacterTableWithStoredGroup, function( arg )
    local G, tbl, arec, ccl, compat, new, i;

    # Get and check the arguments.
    if   Length( arg ) = 2 and IsGroup( arg[1] )
                           and IsOrdinaryTable( arg[2] ) then
      arec:= rec();
    elif Length( arg ) = 3 and IsGroup( arg[1] )
                           and IsOrdinaryTable( arg[2] )
                           and ( IsRecord( arg[3] ) or IsList(arg[3]) ) then
      arec:= arg[3];
    else
      Error( "usage: CharacterTableWithStoredGroup(<G>,<tbl>[,<arec>])" );
    fi;

    G   := arg[1];
    tbl := arg[2];

    if HasOrdinaryCharacterTable( G ) then
      Error( "<G> has already a character table" );
    fi;

    ccl:= ConjugacyClasses( G );
#T How to exploit the known character table
#T if the conjugacy classes of <G> are not yet computed?

    if IsList( arec ) then
      compat:= arec;
    else
      compat:= CompatibleConjugacyClasses( G, ccl, tbl, arec );
    fi;

    if not IsList( compat ) then
      return fail;
    fi;

    # Permute the classes if necessary.
    if compat <> [ 1 .. Length( compat ) ] then
      ccl:= ccl{ compat };
    fi;

    # Create a copy of the table.
    new:= ConvertToLibraryCharacterTableNC(
              rec( UnderlyingCharacteristic := 0 ) );

    # Set the supported attribute values.
    # We may assume that the subobjects of mutable attribute values
    # are already immutable.
    for i in [ 3, 6 .. Length( SupportedCharacterTableInfo ) ] do
      if Tester( SupportedCharacterTableInfo[ i-2 ] )( tbl )
         and SupportedCharacterTableInfo[ i-1 ] <> "Irr" then
        Setter( SupportedCharacterTableInfo[ i-2 ] )( new,
            SupportedCharacterTableInfo[ i-2 ]( tbl ) );
      fi;
    od;

    # Set the irreducibles.
    SetIrr( new, List( Irr( tbl ),
        chi -> Character( new, ValuesOfClassFunction( chi ) ) ) );

    # The identification is unique, store attribute values.
    SetUnderlyingGroup( new, G );
    SetConjugacyClasses( new, ccl );
    SetIdentificationOfConjugacyClasses( new, compat );
    SetOrdinaryCharacterTable( G, new );

    return new;
    end );


#############################################################################
##
#M  CompatibleConjugacyClasses( <G>, <ccl>, <tbl>[, <arec>] )
##
InstallMethod( CompatibleConjugacyClasses,
    "three argument version, call `CompatibleConjugacyClassesDefault'",
    [ IsGroup, IsList, IsOrdinaryTable ],
    function( G, ccl, tbl )
    return CompatibleConjugacyClassesDefault( G, ccl, tbl, rec() );
    end );

InstallMethod( CompatibleConjugacyClasses,
    "four argument version, call `CompatibleConjugacyClassesDefault'",
    [ IsGroup, IsList, IsOrdinaryTable, IsRecord ],
    CompatibleConjugacyClassesDefault );


#############################################################################
##
#M  CompatibleConjugacyClasses( <tbl>[, <arec>] )
##
InstallMethod( CompatibleConjugacyClasses,
    "one argument version, call `CompatibleConjugacyClassesDefault'",
    [ IsOrdinaryTable ],
    function( tbl )
    return CompatibleConjugacyClassesDefault( false, false, tbl, rec() );
    end );

InstallMethod( CompatibleConjugacyClasses,
    "two argument version, call `CompatibleConjugacyClassesDefault'",
    [ IsOrdinaryTable, IsRecord ],
    function( tbl, arec )
    return CompatibleConjugacyClassesDefault( false, false, tbl, arec );
    end );


#############################################################################
##
#F  CompatibleConjugacyClassesDefault( <G>, <ccl>, <tbl>, <arec> )
#F  CompatibleConjugacyClassesDefault( false, false, <tbl>, <arec> )
##
InstallGlobalFunction( CompatibleConjugacyClassesDefault,
    function( G, ccl, tbl, arec )

    local natchar,     # natural character (if known)
          nccl,        # no. of conjugacy classes of `G'
          pi1,         # the partition of positions in `tbl'
          pi2,         # the partition of positions in `ccl'
          bijection,   # partial bijection currently known
          refine,      # function that does the refinement
          tbl_orders,  # element orders of classes in `tbl'
          reps,        # representatives of the classes in `ccl'
          fun1, fun2,  # functions returning invariants
          tbl_classes, # class lengths in `tbl'
          degree,      # degree of the natural character
          derpos,      # positions of classes in the derived subgroup
          primes,      # primedivisors of the group order
          powerclass,
          powerclasses,
          result,      # return value
          usesymm,     # local function to use table automorphisms
          usepowers,   # local function to use power maps
          usegalois,   # local function to use Galois conjugation
          sums,        # list of lengths of entries in `equpos'
          i,
          j,
          symm,        # group of symmetries that is still available
          ords,
          p;

    if IsBound( arec.natchar ) then
      natchar:= arec.natchar;
    fi;

    nccl:= NrConjugacyClasses( tbl );

    if ccl <> false and Length( ccl ) <> nccl then
      return fail;
    fi;

    # We set up two partitions `pi1' of the column positions in `tbl'
    # and `pi2' of the positions in `ccl'
    # such that the $i$-th entries correspond to each other.
    # These partitions are successively refined
    # until either the bijection is found or no more criteria are available.
    # Uniquely identified classes are removed from `pi1' and `pi2',
    # and inserted in `bijection'.
    if IsBound( arec.bijection ) then
      bijection:= ShallowCopy( arec.bijection );
      pi1:= [ Filtered( [ 1 .. nccl ], i -> not IsBound( bijection[i] ) ) ];
      pi2:= [ Difference( [ 1 .. nccl ], bijection ) ];
    else
      bijection:= [];
      pi1:= [ [ 1 .. nccl ] ];
      pi2:= [ [ 1 .. nccl ] ];
    fi;

    # the function that does the refinement,
    # the return value `false' means that the bijection is still ambiguous,
    # `true' means that either the bijection is unique or an inconsistency
    # was detected (in the former case, `result' holds the bijection,
    # in the latter case, `result' is `fail')
    refine:= function( fun1, fun2, range )

      local newpi1, newpi2,
            i, j,
            val1, val2,
            set,
            new1, new2;

      if G = false then
        fun2:= fun1;
      fi;

      for i in range do
        newpi1:= [];
        newpi2:= [];
        val1:= List( pi1[i], fun1 );
        set:= Set( val1 );
        if Length( set ) = 1 then
          new1:= [ pi1[i] ];
          new2:= [ pi2[i] ];
        else
          val2:= List( pi2[i], fun2 );
          if set <> Set( val2 ) then
            Info( InfoCharacterTable, 2,
                  "<G> and <tbl> do not fit together" );
            result:= fail;
            return true;
          fi;
          new1:= List( set, x -> [] );
          new2:= List( set, x -> [] );
          for j in [ 1 .. Length( val1 ) ] do
            Add( new1[ Position( set, val1[j] ) ], pi1[i][j] );
            Add( new2[ Position( set, val2[j] ) ], pi2[i][j] );
          od;
        fi;
        for j in [ 1 .. Length( set ) ] do
          if Length( new1[j] ) <> Length( new2[j] ) then
            Info( InfoCharacterTable, 2,
                  "<G> and <tbl> do not fit together" );
            result:= fail;
            return true;
          fi;
          if Length( new1[j] ) = 1 then
            bijection[ new1[j][1] ]:= new2[j][1];
          else
            Add( newpi1, new1[j] );
            Add( newpi2, new2[j] );
          fi;
        od;
        Append( pi1, newpi1 );
        Append( pi2, newpi2 );
        Unbind( pi1[i] );
        Unbind( pi2[i] );
      od;

      pi1:= Compacted( pi1 );
      pi2:= Compacted( pi2 );

      if IsEmpty( pi1 ) then
        Info( InfoCharacterTable, 2, "unique identification" );
        if G = false then
          result:= [];
        else
          result:= bijection;
        fi;
        return true;
      else
        return false;
      fi;
    end;

    # Use element orders.
    Info( InfoCharacterTable, 2,
          "using element orders to identify classes" );
    tbl_orders:= OrdersClassRepresentatives( tbl );
    if G <> false then
      reps:= List( ccl, Representative );
    fi;
    fun1:= ( i -> tbl_orders[i] );
    fun2:= ( i -> Order( reps[i] ) );
    if refine( fun1, fun2, [ 1 .. Length( pi1 ) ] ) then
      return result;
    fi;

    # Use class lengths.
    Info( InfoCharacterTable, 2,
          "using class lengths to identify classes" );
    tbl_classes:= SizesConjugacyClasses( tbl );
    fun1:= ( i -> tbl_classes[i] );
    fun2:= ( i -> Size( ccl[i] ) );
    if refine( fun1, fun2, [ 1 .. Length( pi1 ) ] ) then
      return result;
    fi;

    # Distinguish classes in the derived subgroup from others.
    derpos:= ClassPositionsOfDerivedSubgroup( tbl );
    if Length( derpos ) <> nccl then

      Info( InfoCharacterTable, 2,
            "using derived subgroup to identify classes" );
      fun1:= ( i -> i in derpos );
      fun2:= ( i -> reps[i] in DerivedSubgroup( G ) );
      if refine( fun1, fun2, [ 1 .. Length( pi1 ) ] ) then
        return result;
      fi;

    fi;

    # Use the natural character if it is prescribed.
    if IsBound( natchar ) then

      Info( InfoCharacterTable, 2,
            "using natural character to identify classes" );
      degree:= natchar[1];
      fun1:= ( i -> natchar[i] );
      if   IsPermGroup( G ) then
        fun2:= ( i -> degree - NrMovedPoints( reps[i] ) );
      elif IsMatrixGroup( G ) then
        fun2:= ( i -> TraceMat( reps[i] ) );
      elif G <> false then
        Info( InfoCharacterTable, 2,
              "<G> is no perm. or matrix group, ignore natural character" );
        fun1:= ReturnTrue;
        fun2:= ReturnTrue;
      fi;
      if refine( fun1, fun2, [ 1 .. Length( pi1 ) ] ) then
        return result;
      fi;

    fi;

    # Use power maps.
    primes:= PrimeDivisors( Size( tbl ) );

    # store power maps of the group, in order to identify the class
    # of the power only once.
    powerclasses:= [];
    powerclass:= function( i, p, choice )
      if not IsBound( powerclasses[p] ) then
        powerclasses[p]:= [];
      fi;
      if not IsBound( powerclasses[p][i] ) then
        powerclasses[p][i]:= First( choice, j -> reps[i]^p in ccl[j] );
      fi;
      return powerclasses[p][i];
    end;

    usepowers:= function( p )

      local pmap, i, img1, pos, j, img2, choice, no, copypi1, k, fun1, fun2;

      Info( InfoCharacterTable, 2, " (p = ", p, ")" );

      pmap:= PowerMap( tbl, p );

      # First consider classes whose image under the bijection is known
      # but for whose `p'-th power the image is not yet known.
      for i in [ 1 .. Length( bijection ) ] do
        img1:= pmap[i];
        if IsBound( bijection[i] ) and not IsBound( bijection[ img1 ] ) then
          pos:= 0;
          for j in [ 1 .. Length( pi1 ) ] do
            if img1 in pi1[j] then
              pos:= j;
              break;
            fi;
          od;
          if G = false then
            img2:= img1;
          else
            img2:= powerclass( bijection[i], p, pi2[ pos ] );
            if img2 = fail then
              result:= fail;
              return true;
            fi;
          fi;
          bijection[ img1 ]:= img2;
          RemoveSet( pi1[ pos ], img1 );
          RemoveSet( pi2[ pos ], img2 );
          if Length( pi1[ pos ] ) = 1 then
            bijection[ pi1[ pos ][1] ]:= pi2[ pos ][1];
            Unbind( pi1[ pos ] );
            Unbind( pi2[ pos ] );
            if IsEmpty( pi1 ) then
              Info( InfoCharacterTable, 2, "unique identification" );
              if G = false then
                result:= [];
              else
                result:= bijection;
              fi;
              return true;
            fi;
            pi1:= Compacted( pi1 );
            pi2:= Compacted( pi2 );
          fi;
        fi;
      od;

      # Next consider each set of nonidentified classes
      # together with its `p'-th powers.
      copypi1:= ShallowCopy( pi1 );
      for i in [ 1 .. Length( copypi1 ) ] do

        choice:= [];
        no:= 0;
        for j in Set( pmap{ copypi1[i] } ) do
          if IsBound( bijection[j] ) then
            AddSet( choice, bijection[j] );
            no:= no + 1;
          else
            pos:= 0;
            for k in [ 1 .. Length( pi1 ) ] do
              if j in pi1[k] then
                pos:= k;
                break;
              fi;
            od;
            if not IsSubset( choice, pi2[ pos ] ) then
              no:= no + 1;
              UniteSet( choice, pi2[ pos ] );
            fi;
          fi;
        od;

        if 1 < no then

          fun1:= function( j )
            local img;
            img:= pmap[j];
            if IsBound( bijection[ img ] ) then
              return AdditiveInverse( bijection[ img ] );
            else
              return First( [ 1 .. Length( pi1 ) ], k -> img in pi1[k] );
            fi;
          end;

          fun2:= function( j )
            local img;
            img:= powerclass( j, p, choice );
            if img in bijection then
              return AdditiveInverse( img );
            else
              return First( [ 1 .. Length( pi2 ) ], k -> img in pi2[k] );
            fi;
          end;

          if refine( fun1, fun2, [ Position( pi1, copypi1[i] ) ] ) then
            return true;
          fi;

        fi;

      od;

      return false;
    end;

    # Use symmetries of the table.
    # (There may be asymmetries because of the prescribed character,
    # so we start with the partition stabilizer of `pi1'.)
    symm:= AutomorphismsOfTable( tbl );
    if IsBound( natchar ) then
      for i in pi1 do
        symm:= Stabilizer( symm, i, OnSets );
      od;
    fi;

    # Sort `pi1' and `pi2' according to decreasing element order.
    # (catch automorphisms for long orbits, hope for powers
    # if ambiguities remain)
    ords:= List( pi1, x -> - tbl_orders[ x[1] ] );
    ords:= Sortex( ords );
    pi1:= Permuted( pi1, ords );
    pi2:= Permuted( pi2, ords );

    # If all points in a part of `pi1' are in the same orbit
    # under table automorphism,
    # we may separate one point from the others.
    usesymm:= function()
      local i, tuple;
      for i in [ 1 .. Length( pi1 ) ] do
        if not IsTrivial( symm ) then
          tuple:= pi1[i];
          if     1 < Length( tuple )
             and tuple = Set( Orbit( symm, tuple[1], OnPoints ) ) then

            Info( InfoCharacterTable, 2,
                  "found useful table automorphism" );
            symm:= Stabilizer( symm, tuple[1] );
            bijection[ tuple[1] ]:= pi2[i][1];
            RemoveSet( pi1[i], pi1[i][1] );
            RemoveSet( pi2[i], pi2[i][1] );
            if Length( pi1[i] ) = 1 then
              bijection[ pi1[i][1] ]:= pi2[i][1];
              Unbind( pi1[i] );
              Unbind( pi2[i] );
            fi;

          fi;
        fi;
      od;
      if IsEmpty( pi1 ) then
        Info( InfoCharacterTable, 2, "unique identification" );
        if G = false then
          result:= [];
        else
          result:= bijection;
        fi;
        return true;
      fi;
      pi1:= Compacted( pi1 );
      pi2:= Compacted( pi2 );

      return false;
    end;

    # Use Galois conjugacy of classes.
    usegalois:= function()

      local galoisfams, copypi1, i, list, fam, id, im, res, pos, fun1, fun2;

      galoisfams:= GaloisMat( TransposedMat( Irr( tbl ) ) ).galoisfams;
      galoisfams:= List( Filtered( galoisfams, IsList ), x -> x[1] );

      copypi1:= ShallowCopy( pi1 );

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

        list:= copypi1[i];
        fam:= First( galoisfams, x -> IsSubset( x, list ) );
        if fam <> fail then
          id:= First( fam, j -> IsBound( bijection[j] ) );
          if id <> fail then

            Info( InfoCharacterTable, 2,
                  "found useful Galois automorphism" );
            im:= bijection[ id ];
            res:= PrimeResidues( tbl_orders[ id ] );
            RemoveSet( res, 1 );
            pos:= Position( pi1, copypi1[i] );
            fun1:= ( j -> First( res, k -> PowerMap( tbl, k, id ) = j ) );
            fun2:= ( j -> First( res,
                             k -> powerclass( im, k, pi2[ pos ] ) = j ) );
            if refine( fun1, fun2, [ pos ] ) then
              return true;
            fi;

          fi;
        fi;

      od;

      return false;
    end;

    repeat

      sums:= List( pi1, Length );

      Info( InfoCharacterTable, 2,
            "trying power maps to identify classes" );
      for p in primes do
        if usepowers( p ) then
          return result;
        fi;
      od;

      if usesymm() then
        return result;
      fi;

      if usegalois() then
        return result;
      fi;

    until sums = List( pi1, Length );

    # no identification yet ...
    Info( InfoCharacterTable, 2,
          "not identified classes: ", pi1 );
    if G = false then
      return pi1;
    else
      return fail;
    fi;
end );


#############################################################################
##
##  4. Operators for Character Tables
##


#############################################################################
##
#M  \mod( <ordtbl>, <p> ) . . . . . . . . . . . . . . . . . <p>-modular table
##
InstallMethod( \mod,
    "for ord. char. table, and pos. integer (call `BrauerTable')",
    [ IsOrdinaryTable, IsPosInt ],
    BrauerTable );


#############################################################################
##
#M  \*( <tbl1>, <tbl2> )  . . . . . . . . . . . . .  direct product of tables
##
InstallOtherMethod( \*,
    "for two nearly character tables (call `CharacterTableDirectProduct')",
    [ IsNearlyCharacterTable, IsNearlyCharacterTable ],
    CharacterTableDirectProduct );


#############################################################################
##
#M  \/( <tbl>, <list> )  . . . . . . . . .  character table of a factor group
##
InstallOtherMethod( \/,
    "for char. table, and positions list (call `CharacterTableFactorGroup')",
    [ IsNearlyCharacterTable, IsList and IsCyclotomicCollection ],
    CharacterTableFactorGroup );


#############################################################################
##
##  5. Attributes and Properties for Groups as well as for Character Tables
##


#############################################################################
##
#M  CharacterDegrees( <G> ) . . . . . . . . . . . . . . . . . . . for a group
#M  CharacterDegrees( <G>, <zero> ) . . . . . . . . . .  for a group and zero
##
##  - The two-argument version with second argument zero
##    delegates to the one-argument version.
##
##  - The two-argument version with second argument a positive integer <p>
##    has one method that
##    - calls the one-argument version if <p> does not divide the group order,
##    - calls 'CharacterDegreesAbelian' if the group is abelian,
##    - uses stored irreducibles of the Brauer character table in question,
##    - calls 'CharacterDegreesConlon' if the group is solvable,
##    - and delegates to the Brauer character table in question otherwise
##      (which may result in an error if the degrees  cannot be computed).
##
##  - The one-argument version has at least the following methods,
##    listed according to decreasing rank:
##    - system getter,
##    - applicable to 'IsGroup and IsAbelian',
##    - applicable to 'IsGroup and HasIrr',
##    - applicable to 'IsGroup and HasOrdinaryCharacterTable'
##      (call 'TryNextMethod()' if the table does not store irreducibles),
##    - applicable to 'IsGroup and IsHandledByNiceMonomorphism'
##      (gets installed via 'AttributeMethodByNiceMonomorphism'),
##    - applicable to 'IsGroup and MayBeHandledByNiceMonomorphism'
##      (gets installed via 'AttributeMethodByNiceMonomorphism'),
##    - applicable to 'IsGroup'
##      (this method decides about the algorithm to be used).
##
InstallMethod( CharacterDegrees,
    "for a group, and zero (call the one-argument version)",
    [ IsGroup, IsZeroCyc ],
    { G, zero } -> List( CharacterDegrees( G ), ShallowCopy ) );

BindGlobal( "CharacterDegreesAbelian", function( G, p )
    G:= Size( G );
    if p <> 0 then
      while G mod p = 0 do
        G:= G / p;
      od;
    fi;
    return [ [ 1, G ] ];
    end );

InstallMethod( CharacterDegrees,
    "for an abelian group",
    [ IsGroup and IsAbelian ],
    {} -> RankFilter( IsHandledByNiceMonomorphism ), # override nice mon. method
    G -> CharacterDegreesAbelian( G, 0 ) );

InstallMethod( CharacterDegrees,
    "for a group with known Irr value",
    [ IsGroup and HasIrr ],
    {} -> RankFilter( IsHandledByNiceMonomorphism ) + 1, # override nice mon. method
    G -> Collected( List( Irr( G ), DegreeOfCharacter ) ) );

InstallMethod( CharacterDegrees,
    "for a group with known OrdinaryCharacterTable value",
    [ IsGroup and HasOrdinaryCharacterTable ],
    {} -> RankFilter( IsHandledByNiceMonomorphism ), # override nice mon. method
    function( G )
    G:= OrdinaryCharacterTable( G );
    if not HasIrr( G ) then
      TryNextMethod();
    fi;
    return Collected( List( Irr( G ), DegreeOfCharacter ) );
    end );

InstallMethod( CharacterDegrees,
    "for a group",
    [ IsGroup ],
    function( G )
    # We assume that the 'Irr' value is not known,
    # otherwise a method with higher rank would have been successful.
    if IsAbelian( G ) then
      return CharacterDegreesAbelian( G, 0 );
    elif IsSupersolvableGroup( G ) then
      return CharacterDegreesBaumClausen( G );
    elif IsSolvableGroup( G ) then
      return CharacterDegreesConlon( G, 0 );
    else
      # We have no better methods.
      return Collected( List( Irr( G ), DegreeOfCharacter ) );
    fi;
    end );


#############################################################################
##
#M  CharacterDegrees( <G>, <p> )  . . . . . . . . . . . . . . . for prime <p>
##
InstallMethod( CharacterDegrees,
    "for a group, and positive integer",
    [ IsGroup, IsPosInt ],
    function( G, p )
    local tbl, modtbl;

    Assert( 1, IsPrimeInt( p ) );
    if Size( G ) mod p <> 0 then
      return List( CharacterDegrees( G ), ShallowCopy );
    elif IsAbelian( G ) then
      return CharacterDegreesAbelian( G, p );
    elif HasOrdinaryCharacterTable( G ) then
      # Perhaps the 'p'-modular irreducibles are stored.
      tbl:= CharacterTable( G );
      if IsBound( ComputedBrauerTables( tbl )[p] ) then
        modtbl:= ComputedBrauerTables( tbl )[p];
        if HasIrr( modtbl ) then
          return List( CharacterDegrees( modtbl ), ShallowCopy );
        fi;
      fi;
    fi;
    if IsSolvableGroup( G ) then
      return CharacterDegreesConlon( G, p );
    else
      # Perhaps we cannot compute the result.
      return List( CharacterDegrees( CharacterTable( G, p ) ), ShallowCopy );
    fi;
    end );


#############################################################################
##
#M  CharacterDegrees( <tbl> ) . . . . . . . . . . . . . for a character table
##
##  If the table knows its group and the irreducibles are not yet stored then
##  we try to avoid the computation of the irreducibles and therefore
##  delegate to the group.
##  Otherwise we use the irreducibles.
##
InstallMethod( CharacterDegrees,
    "for a character table",
    [ IsCharacterTable ],
    function( tbl )
    if HasUnderlyingGroup( tbl ) and not HasIrr( tbl ) then
      return CharacterDegrees( UnderlyingGroup( tbl ),
                               UnderlyingCharacteristic( tbl ) );
    else
      return Collected( List( Irr( tbl ), DegreeOfCharacter ) );
    fi;
    end );


#############################################################################
##
#M  CharacterDegrees( <G> ) . . . . . for group handled via nice monomorphism
##
AttributeMethodByNiceMonomorphism( CharacterDegrees, [ IsGroup ] );


#############################################################################
##
#F  CommutatorLength( <tbl> ) . . . . . . . . . . . . . for a character table
##
InstallMethod( CommutatorLength,
    "for a character table",
    [ IsCharacterTable ],
    function( tbl )

    local nccl,
          irr,
          derived,
          commut,
          other,
          n,
          G_n,
          new,
          i;

    # Compute the classes that form the derived subgroup of $G$.
    irr:= Irr( tbl );
    nccl:= Length( irr );
    derived:= Intersection( List( LinearCharacters( tbl ),
                                  ClassPositionsOfKernel ) );
    commut:= Filtered( [ 1 .. nccl ],
                 i -> Sum( irr, chi -> chi[i] / chi[1] ) <> 0 );
    other:= Difference( derived, commut );

    # Loop.
    n:= 1;
    G_n:= derived;
    while not IsEmpty( other ) do
      new:= [];
      for i in other do
        if ForAny( derived, j -> ForAny( G_n,
            k -> ClassMultiplicationCoefficient( tbl, j, k, i ) <> 0 ) ) then
          Add( new, i );
        fi;
      od;
      n:= n+1;
      UniteSet( G_n, new );
      SubtractSet( other, new );
    od;

    return n;
    end );


#############################################################################
##
#M  CommutatorLength( <G> )  . . . . . . . . . . . . . . . . . .  for a group
##
InstallMethod( CommutatorLength,
    "for a group",
    [ IsGroup ],
    G -> CommutatorLength( CharacterTable( G ) ) );


#############################################################################
##
#M  Irr( <G> )  . . . . . . . . . . . . . . . . . . . . . . . . . for a group
##
##  Delegate to the two-argument version.
##
InstallMethod( Irr,
    "for a group (call the two-argument version)",
    [ IsGroup ],
    G -> Irr( G, 0 ) );


#############################################################################
##
#M  Irr( <G>, <0> )   . . . . . . . . . . . . . . . . .  for a group and zero
##
##  We compute the character table of <G> if it is not yet stored
##  (which must be done anyhow), and then check whether the table already
##  knows its irreducibles.
##  This method is successful if the method for computing the table (head)
##  automatically computes also the irreducibles.
##
InstallMethod( Irr,
    "partial method for a group, and zero",
    [ IsGroup, IsZeroCyc ], SUM_FLAGS,
    function( G, zero )
    local tbl;
    tbl:= OrdinaryCharacterTable( G );
    if HasIrr( tbl ) then
      return Irr( tbl );
    else
      TryNextMethod();
    fi;
    end );


#############################################################################
##
#M  Irr( <G>, <p> )   . . . . . . . . . . . . . . . . for a group and a prime
##
InstallMethod( Irr,
    "for a group, and a prime",
    [ IsGroup, IsPosInt ],
    function( G, p )
    return Irr( BrauerTable( G, p ) );
    end );


#############################################################################
##
#M  Irr( <modtbl> ) . . . . . . . . . . . . . for a <p>-solvable Brauer table
##
##  Compute the modular irreducibles from the ordinary irreducibles
##  using the Fong-Swan Theorem.
##
InstallMethod( Irr,
    "for a <p>-solvable Brauer table (use the Fong-Swan Theorem)",
    [ IsBrauerTable ],
    function( modtbl )
    local p,       # characteristic
          ordtbl,  # ordinary character table
          rest,    # restriction of characters to `p'-regular classes
          irr,     # list of Brauer characters
          cd,      # list of ordinary character degrees
          chars,   # nonlinear characters distributed by degree
          i,       # loop variable
          deg,     # one character degree
          pos,     # position of a degree
          list,    # characters of one degree
          dec;     # decomposition of ordinary characters
                   # into known Brauer characters

    p:= UnderlyingCharacteristic( modtbl );
    ordtbl:= OrdinaryCharacterTable( modtbl );

    if not IsPSolvableCharacterTable( ordtbl, p ) then
      TryNextMethod();
    fi;

    rest:= RestrictedClassFunctions( Irr( ordtbl ), modtbl );

    if Size( ordtbl ) mod p <> 0 then

      # Catch a trivial case.
      irr:= rest;

    else

      # Start with the linear characters.
      # (Choose the same succession as in the ordinary table,
      # in particular leave the trivial character at first position
      # if this is the case for `ordtbl'.)
      irr:= [];
      cd:= [];
      chars:= [];
      for i in rest do
        deg:= DegreeOfCharacter( i );
        if deg = 1 then
          if not i in irr then
            Add( irr, i );
          fi;
        else
          pos:= Position( cd, deg );
          if pos = fail then
            Add( cd, deg );
            Add( chars, [ i ] );
          elif not i in chars[ pos ] then
            Add( chars[ pos ], i );
          fi;
        fi;
      od;
      SortParallel( cd, chars );

      for list in chars do
        dec:= Decomposition( irr, list, "nonnegative" );
        for i in [ 1 .. Length( dec ) ] do
          if dec[i] = fail then
            Add( irr, list[i] );
          fi;
        od;
      od;

    fi;

    # Return the irreducible Brauer characters.
    return irr;
    end );


#############################################################################
##
#M  Irr( <ordtbl> ) . . . . . . . .  for an ord. char. table with known group
##
##  We must delegate this to the underlying group.
##  Note that the ordering of classes for the characters in the group
##  and the characters in the table may be different!
##  Note that <ordtbl> may have been obtained by sorting the classes of the
##  table stored as the `OrdinaryCharacterTable' value of $G$;
##  In this case, the attribute `ClassPermutation' of <ordtbl> is set.
##  (The `OrdinaryCharacterTable' value of $G$ itself does *not* have this.)
##
InstallMethod( Irr,
    "for an ord. char. table with known group (delegate to the group)",
    [ IsOrdinaryTable and HasUnderlyingGroup ],
    function( ordtbl )
    local irr, pi;
    irr:= Irr( UnderlyingGroup( ordtbl ) );
    if HasClassPermutation( ordtbl ) then
      pi:= ClassPermutation( ordtbl );
      irr:= List( irr, chi -> Character( ordtbl,
                Permuted( ValuesOfClassFunction( chi ), pi ) ) );
    fi;
    return irr;
    end );


#############################################################################
##
#M  IBr( <modtbl> ) . . . . . . . . . . . . . .  for a Brauer character table
#M  IBr( <G>, <p> ) . . . . . . . . . . . .  for a group, and a prime integer
##
InstallMethod( IBr,
    "for a Brauer table",
    [ IsBrauerTable ],
    Irr );

InstallMethod( IBr,
    "for a group, and a prime integer",
    [ IsGroup, IsPosInt ],
    function( G, p ) return Irr( G, p ); end );


#############################################################################
##
#M  SetIrr( <tbl>, <list> ) . . . . . . . . . . . . . . for a character table
#M  SetIrr( <G>, <list> ) . . . . . . . . . . . . . . . . . . . . for a group
##
##  Provide a special setter method that sets the irreducibility flag in the
##  characters.
##
InstallMethod( SetIrr,
    "set the irreducibility flag",
    [ IsCharacterTable, IsList ],
    function( tbl, irr )
    local chi;

    for chi in irr do
      SetIsIrreducibleCharacter( chi, true );
    od;

    TryNextMethod();
    end );

InstallMethod( SetIrr,
    "set the irreducibility flag",
    [ IsGroup, IsList ],
    function( G, irr )
    local chi;

    for chi in irr do
      SetIsIrreducibleCharacter( chi, true );
    od;

    TryNextMethod();
    end );


#############################################################################
##
#M  LinearCharacters( <G> )
##
##  Delegate to the two-argument version, as for `Irr'.
##
InstallMethod( LinearCharacters,
    "for a group (call the two-argument version)",
    [ IsGroup ],
    G -> LinearCharacters( G, 0 ) );


#############################################################################
##
#M  LinearCharacters( <G>, 0 )
##
InstallMethod( LinearCharacters,
    "for a group, and zero",
    [ IsGroup, IsZeroCyc ],
    function( G, zero )
    local tbl, pi, img, fus, res, chi;

    if HasOrdinaryCharacterTable( G ) then
      tbl:= OrdinaryCharacterTable( G );
      if HasIrr( tbl ) then
        return LinearCharacters( tbl );
      fi;
    fi;
    if IsAbelian( G ) then
      return Irr( G, 0 );
    fi;

    pi:= NaturalHomomorphismByNormalSubgroupNC( G, DerivedSubgroup( G ) );
    img:= ImagesSource( pi );
    SetIsAbelian( img, true );
#   return RestrictedClassFunctions( CharacterTable( img ),
#              Irr( img, 0 ), pi );
# We cannot use this because the source of `pi' may be not identical with `G'!
    fus:= FusionConjugacyClasses( pi );
    tbl:= CharacterTable( G );
    res:= List( Irr( img, 0 ), x -> Character( tbl, x{ fus } ) );
    for chi in res do
      SetIsIrreducibleCharacter( chi, true );
    od;
    return res;
    end );


#############################################################################
##
#M  LinearCharacters( <G>, <p> )
##
InstallMethod( LinearCharacters,
    "for a group, and positive integer",
    [ IsGroup, IsPosInt ],
    function( G, p )
    local ordt, modt, res, chi;

    if not IsPrimeInt( p ) then
      Error( "<p> must be a prime" );
    fi;

    ordt:= OrdinaryCharacterTable( G );
    modt:= BrauerTable( ordt, p );
    res:= DuplicateFreeList(
              RestrictedClassFunctions( LinearCharacters( ordt ), modt ) );

    for chi in res do
      SetIsIrreducibleCharacter( chi, true );
    od;
    return res;
    end );


#############################################################################
##
#M  LinearCharacters( <ordtbl> )  . . . . . . . . . . . for an ordinary table
##
InstallMethod( LinearCharacters,
    "for an ordinary table",
    [ IsOrdinaryTable ],
    function( ordtbl )
    local lin, pi, chi;
    if HasIrr( ordtbl ) then
      return Filtered( Irr( ordtbl ), chi -> chi[1] = 1 );
    elif HasUnderlyingGroup( ordtbl ) then
      lin:= LinearCharacters( UnderlyingGroup( ordtbl ) );
      if HasClassPermutation( ordtbl ) then
        pi:= ClassPermutation( ordtbl );
        lin:= List( lin, lambda -> Character( ordtbl,
                  Permuted( ValuesOfClassFunction( lambda ), pi ) ) );
      fi;
      for chi in lin do
        SetIsIrreducibleCharacter( chi, true );
      od;
      return lin;
    else
      TryNextMethod();
    fi;
    end );


#############################################################################
##
#M  LinearCharacters( <modtbl> )  . . . . . . . . . . . .  for a Brauer table
##
InstallMethod( LinearCharacters,
    "for a Brauer table",
    [ IsBrauerTable ],
    function( modtbl )
    local res, chi;

    res:= DuplicateFreeList( RestrictedClassFunctions(
                  LinearCharacters( OrdinaryCharacterTable( modtbl ) ),
                  modtbl ) );
    for chi in res do
      SetIsIrreducibleCharacter( chi, true );
    od;
    return res;
    end );


#############################################################################
##
#M  OrdinaryCharacterTable( <G> ) . . . . . . . . . . . . . . . . for a group
#M  OrdinaryCharacterTable( <modtbl> )  . . . .  for a Brauer character table
##
##  In the first case, we setup the table object.
##  In the second case, we delegate to `OrdinaryCharacterTable' for the
##  group.
##
InstallMethod( OrdinaryCharacterTable,
    "for a group",
    [ IsGroup ],
    function( G )
    local tbl, ccl, idpos, bijection;

    # Make the object.
    tbl:= Objectify( NewType( NearlyCharacterTablesFamily,
                              IsOrdinaryTable and IsAttributeStoringRep ),
                     rec() );

    # Store the attribute values of the interface.
    SetUnderlyingGroup( tbl, G );
    SetUnderlyingCharacteristic( tbl, 0 );
    IsFinite(G);
    ccl:= ConjugacyClasses( G );
    idpos:= First( [ 1 .. Length( ccl ) ],
                   i -> Order( Representative( ccl[i] ) ) = 1 );
    if idpos = 1 then
      bijection:= [ 1 .. Length( ccl ) ];
    else
      ccl:= Concatenation( [ ccl[ idpos ] ], ccl{ [ 1 .. idpos-1 ] },
                           ccl{ [ idpos+1 .. Length( ccl ) ] } );
      bijection:= Concatenation( [ idpos ], [ 1 .. idpos-1 ],
                                 [ idpos+1 .. Length( ccl ) ] );
    fi;
    SetConjugacyClasses( tbl, ccl );
    SetIdentificationOfConjugacyClasses( tbl, bijection );

    # Return the table.
    return tbl;
    end );


##############################################################################
##
#M  AbelianInvariants( <tbl> )  . . . . . . . for an ordinary character table
##
##  For all Sylow $p$ subgroups of the factor of <tbl> by the normal subgroup
##  given by `ClassPositionsOfDerivedSubgroup( <tbl> )',
##  compute the abelian invariants by repeated factoring by a cyclic group
##  of maximal order.
##
InstallMethod( AbelianInvariants,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    function( tbl )

    local kernel,  # cyclic group to be factored out
          inv,     # list of invariants, result
          primes,  # list of prime divisors of actual size
          max,     # list of actual maximal orders, for `primes'
          pos,     # list of positions of maximal orders
          orders,  # list of representative orders
          i,       # loop over classes
          j;       # loop over primes

    # Do all computations modulo the derived subgroup.
    kernel:= ClassPositionsOfDerivedSubgroup( tbl );
    if 1 < Length( kernel ) then
      tbl:= tbl / kernel;
    fi;
#T cheaper to use only orders and power maps,
#T and to avoid computing several tables!
#T (especially avoid to compute the irreducibles of the original
#T table if they are not known!)

    inv:= [];

    while 1 < Size( tbl ) do

      # For all prime divisors $p$ of the size,
      # compute the element of maximal $p$ power order.
      primes:= PrimeDivisors( Size( tbl ) );
      max:= List( primes, x -> 1 );
      pos:= [];
      orders:= OrdersClassRepresentatives( tbl );
      for i in [ 2 .. Length( orders ) ] do
        if IsPrimePowerInt( orders[i] ) then
          j:= 1;
          while orders[i] mod primes[j] <> 0 do
            j:= j+1;
          od;
          if orders[i] > max[j] then
            max[j]:= orders[i];
            pos[j]:= i;
          fi;
        fi;
      od;

      # Update the list of invariants.
      Append( inv, max );

      # Factor out the cyclic subgroup.
      tbl:= tbl / ClassPositionsOfNormalClosure( tbl, pos );

    od;

    return AbelianInvariantsOfList( inv );
#T if we call this function anyhow, we can also take factors by the largest
#T cyclic subgroup of the commutator factor group!
    end );


#############################################################################
##
#M  Exponent( <tbl> ) . . . . . . . . . . . . for an ordinary character table
##
InstallMethod( Exponent,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    tbl -> Lcm( OrdersClassRepresentatives( tbl ) ) );


#############################################################################
##
#M  IsAbelian( <tbl> )  . . . . . . . . . . . for an ordinary character table
##
InstallMethod( IsAbelian,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    tbl -> Size( tbl ) = NrConjugacyClasses( tbl ) );


#############################################################################
##
#M  IsCyclic( <tbl> ) . . . . . . . . . . . . for an ordinary character table
##
InstallMethod( IsCyclic,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    tbl -> Size( tbl ) in OrdersClassRepresentatives( tbl ) );


#############################################################################
##
#M  IsElementaryAbelian( <tbl> )  . . . . . . for an ordinary character table
##
InstallMethod( IsElementaryAbelian,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    tbl -> Size( tbl ) = 1 or
           ( IsAbelian( tbl ) and IsPrimeInt( Exponent( tbl ) ) ) );


#############################################################################
##
#M  IsFinite( <tbl> ) . . . . . . . . . . . . for an ordinary character table
##
InstallMethod( IsFinite,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    tbl -> IsInt( Size( tbl ) ) );


#############################################################################
##
#M  IsMonomialCharacterTable( <tbl> ) . . . . for an ordinary character table
##
InstallMethod( IsMonomialCharacterTable,
    "for an ordinary character table with underlying group",
    [ IsOrdinaryTable and HasUnderlyingGroup ],
    tbl -> IsMonomialGroup( UnderlyingGroup( tbl ) ) );


#############################################################################
##
#F  CharacterTable_IsNilpotentFactor( <tbl>, <N> )
##
InstallGlobalFunction( CharacterTable_IsNilpotentFactor, function( tbl, N )
    local series;
    series:= CharacterTable_UpperCentralSeriesFactor( tbl, N );
    return Length( Last(series) ) = NrConjugacyClasses( tbl );
    end );


#############################################################################
##
#M  IsNilpotentCharacterTable( <tbl> )
##
InstallMethod( IsNilpotentCharacterTable,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    function( tbl )
    local series;
    series:= ClassPositionsOfUpperCentralSeries( tbl );
    return Length( Last(series) ) = NrConjugacyClasses( tbl );
    end );


#############################################################################
##
#M  IsPerfectCharacterTable( <tbl> )  . . . . for an ordinary character table
##
InstallMethod( IsPerfectCharacterTable,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    tbl -> Number( Irr( tbl ), chi -> chi[1] = 1 ) = 1 );


#############################################################################
##
#M  IsSimpleCharacterTable( <tbl> ) . . . . . for an ordinary character table
##
##  Avoid computing all normal subgroups of abelian groups and p-groups.
##
InstallMethod( IsSimpleCharacterTable,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    function( tbl )
    if IsAbelian( tbl ) then
      return IsPrimeInt( Size( tbl ) );
    else
      return not IsPrimePowerInt( Size( tbl ) ) and
             Length( ClassPositionsOfNormalSubgroups( tbl ) ) = 2;
    fi;
    end );


#############################################################################
##
#M  IsAlmostSimpleCharacterTable( <tbl> ) . . for an ordinary character table
##
##  <ManSection>
##  <Meth Name="IsAlmostSimpleCharacterTable" Arg="tbl"/>
##
##  <Description>
##  Given the ordinary character table of a group <M>G</M>,
##  we can check whether <M>G</M> has a unique minimal normal subgroup.
##  <P/>
##  The simplicity and nonabelianness of this normal subgroup can be verified
##  by showing that its order occurs as the order of
##  a nonabelian simple group.
##  Note that any minimal normal subgroup is the direct product of
##  isomorphic simple groups,
##  and by a result in <Cite Key="KimmerleLyonsSandlingTeague90"/>,
##  no proper power of the order of a simple group is the order of a simple
##  group.
##  <P/>
##  A finite group is almost simple if and only if it has a unique minimal
##  normal subgroup <M>N</M> with the property that <M>N</M> is nonabelian
##  and simple.
##  (Note that in the this case, the centralizer of <M>N</M> is trivial,
##  because otherwise it would contain a minimal normal subgroup different
##  from <M>N</M>; so <M>G / N</M> acts as a group of outer automorphisms on
##  <M>N</M>.)
##  </Description>
##  </ManSection>
##
##  Note that we could detect also whether a table belongs to an extension of
##  a simple group of prime order by outer automorphisms.
##  (These groups are not regarded as almost simple.)
##  Namely, such a group has a unique minimal normal subgroup <M>N</M> of
##  prime order <M>p</M>
##  and all nontrivial conjugacy classes of <M>G</M> inside <M>N</M>
##  have length <M>[G:N]</M>.
##
InstallMethod( IsAlmostSimpleCharacterTable,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    function( ordtbl )
    local nsg, orbs;

    nsg:= ClassPositionsOfMinimalNormalSubgroups( ordtbl );
    if Length( nsg ) <> 1 then
      return false;
    fi;
    orbs:= SizesConjugacyClasses( ordtbl ){ nsg[1] };
    nsg:= Sum( orbs );

    # An extension of a group of prime order by a subgroup of its
    # automorphism group is *not* regarded as an almost simple group.
    # (We could detect these groups from `orbs', i.e., the class lengths
    # in the minimal normal subgroup.)
    return     ( not IsPrimeInt( nsg ) )
           and IsomorphismTypeInfoFiniteSimpleGroup( nsg ) <> fail;
    end );


#############################################################################
##
#M  IsQuasisimpleCharacterTable( <tbl> )  . . for an ordinary character table
##
InstallMethod( IsQuasisimpleCharacterTable,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    ordtbl -> IsPerfectCharacterTable( ordtbl ) and
       IsSimpleCharacterTable( ordtbl / ClassPositionsOfCentre( ordtbl ) ) );


#############################################################################
##
#M  IsSolvableCharacterTable( <tbl> ) . . . . for an ordinary character table
##
InstallMethod( IsSolvableCharacterTable,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    tbl -> IsPSolvableCharacterTable( tbl, 0 ) );


#############################################################################
##
#M  IsSporadicSimpleCharacterTable( <tbl> ) . for an ordinary character table
##
##  Note that by the classification of finite simple groups, the sporadic
##  simple groups are determined by their orders.
##
InstallMethod( IsSporadicSimpleCharacterTable,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    function( tbl )
    local info;

    if IsSimpleCharacterTable( tbl ) then
      info:= IsomorphismTypeInfoFiniteSimpleGroup( Size( tbl ) );
      return     info <> fail
             and IsBound( info.series )
             and info.series = "Spor";
    fi;
    return false;
    end );


#############################################################################
##
#M  IsSupersolvableCharacterTable( <tbl> )  . for an ordinary character table
##
InstallMethod( IsSupersolvableCharacterTable,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    tbl -> Size( ClassPositionsOfSupersolvableResiduum( tbl ) ) = 1 );


#############################################################################
##
#F  IsomorphismTypeInfoFiniteSimpleGroup( <tbl> )
##
##  The simplicity of the group with character table <A>tbl</A> can be
##  checked.
##  If there is only one simple group of the given order then we are done.
##  Otherwise there are exactly two possibilities,
##  and we distinguish them using the same arguments as in the function for
##  groups.
##  Namely, the group <M>A_8</M> contains an element (of order <M>5</M>)
##  whose centralizer order is <M>15</M>, whereas the group <M>L_3(4)</M>
##  does not have an element with this centralizer order,
##  and the groups in the two infinite series <M>O(2n+1,q)</M> and
##  <M>S(2n,q)</M>, where <M>q</M> is a power of the (odd) prime <M>p</M>,
##  can be distinguished by the fact that in the latter group, any
##  element of order <M>p</M> in the centre of the Sylow <M>p</M>-subgroup
##  has centralizer order divisible by <M>q^{{2n-2}} - 1</M>, whereas no such
##  elements exist in the former group.
##  (Note that <M>n</M> and <M>p</M> can be computed from the order of
##  <M>O(2n+1,q)</M> or <M>S(2n,q)</M>).
##
InstallMethod( IsomorphismTypeInfoFiniteSimpleGroup,
    [ "IsOrdinaryTable" ],
    function( tbl )
    local size, type, n, q, p, sylord, pos;

    if not IsSimpleCharacterTable( tbl ) then
      return fail;
    fi;
    size:= Size( tbl );
    type:= IsomorphismTypeInfoFiniteSimpleGroup( size );
    if IsRecord( type ) and not IsBound( type.series ) then
      # There are two simple groups of the given order.
      if size <> 20160 then
        # Distinguish the two possibilities in the same way as the groups
        # are distinguished by `IsomorphismTypeInfoFiniteSimpleGroup'.
        n:= type.parameter[1];
        q:= type.parameter[2];
        p:= Factors( q )[1];
        sylord:= 1;
        while size mod p = 0 do
          sylord:= sylord * p;
          size:= size / p;
        od;
        pos:= First( [ 1 .. NrConjugacyClasses( tbl ) ],
                     i ->     OrdersClassRepresentatives( tbl )[i] = p
                          and SizesCentralizers( tbl )[i] mod sylord = 0 );
        if SizesCentralizers( tbl )[ pos ] mod (q^(2*n-2)-1) <> 0 then
          type:= rec( series:= "B",
                      parameter:= [ n, q ],
                      name:= Concatenation( "B(", String(n), ",", String(q),
                                            ") ", "= O(", String(2*n+1), ",",
                                            String(q), ")" ),
                      shortname:= Concatenation( "O", String( 2*n+1 ), "(",
                                                 String(q), ")" ) );
        else
          type:= rec( series:= "C",
                      parameter:= [ n, q ],
                      name:= Concatenation( "C(", String(n), ",", String(q),
                                            ") ", "= S(", String(2*n), ",",
                                            String(q), ")" ),
                      shortname:= Concatenation( "S", String( 2*n ), "(",
                                                 String( q ), ")" ) );
        fi;
      elif 15 in SizesCentralizers( tbl ) then
        type:= rec( series:= "A",
                    parameter:= 8,
                    name:= Concatenation( "A(8) ", "~ A(3,2) = L(4,2) ",
                                          "~ D(3,2) = O+(6,2)" ),
                    shortname:= "A8" );
      else
        type:= rec( series:= "L",
                    parameter:= [ 3, 4 ],
                    name:= "A(2,4) = L(3,4)",
                    shortname:= "L3(4)" );
      fi;
    fi;
    return type;
    end );


#############################################################################
##
#M  NrConjugacyClasses( <ordtbl> )  . . . . . for an ordinary character table
#M  NrConjugacyClasses( <modtbl> )  . . . . . .  for a Brauer character table
#M  NrConjugacyClasses( <G> )
##
##  We delegate from <tbl> to the underlying group in the general case.
##  If we know the centralizer orders or class lengths, however, we use them.
##
##  If the argument is a group, we can use the known class lengths of the
##  known ordinary character table.
##
InstallMethod( NrConjugacyClasses,
    "for an ordinary character table with underlying group",
    [ IsOrdinaryTable and HasUnderlyingGroup ],
    ordtbl -> NrConjugacyClasses( UnderlyingGroup( ordtbl ) ) );

InstallMethod( NrConjugacyClasses,
    "for a Brauer character table",
    [ IsBrauerTable ],
    modtbl -> Length( GetFusionMap( modtbl,
                                    OrdinaryCharacterTable( modtbl ) ) ) );

InstallMethod( NrConjugacyClasses,
    "for a character table with known centralizer orders",
    [ IsNearlyCharacterTable and HasSizesCentralizers ],
    tbl -> Length( SizesCentralizers( tbl ) ) );

InstallMethod( NrConjugacyClasses,
    "for a character table with known class lengths",
    [ IsNearlyCharacterTable and HasSizesConjugacyClasses ],
    tbl -> Length( SizesConjugacyClasses( tbl ) ) );

InstallMethod( NrConjugacyClasses,
    "for a group with known ordinary character table",
    [ IsGroup and HasOrdinaryCharacterTable ],
    function( G )
    local tbl;
    tbl:= OrdinaryCharacterTable( G );
    if HasNrConjugacyClasses( tbl ) then
      return NrConjugacyClasses( tbl );
    else
      TryNextMethod();
    fi;
    end );


#############################################################################
##
#M  Size( <tbl> ) . . . . . . . . . . . . . . . . . . . for a character table
#M  Size( <G> )
##
##  We delegate from <tbl> to the underlying group if this is stored.
##  If we know the centralizer orders or class lengths, we may use them.
##
##  If the argument is a group <G>, we can use the known size of the
##  known ordinary character table of <G>.
##
InstallMethod( Size,
    "for a character table",
    [ IsNearlyCharacterTable ],
    function( tbl )
    if HasSizesCentralizers( tbl ) then
      return SizesCentralizers( tbl )[1];
    elif HasUnderlyingGroup( tbl ) and HasSize( UnderlyingGroup( tbl ) ) then
      return Size( UnderlyingGroup( tbl ) );
    elif HasSizesConjugacyClasses( tbl ) then
      return Sum( SizesConjugacyClasses( tbl ) );
    elif HasIrr( tbl ) then
      return SizesCentralizers( tbl )[1];
    elif HasUnderlyingGroup( tbl ) then
      return Size( UnderlyingGroup( tbl ) );
    else
      TryNextMethod();
    fi;
    end );

InstallMethod( Size,
    "for a group with known ordinary character table",
    [ IsGroup and HasOrdinaryCharacterTable ],
    function( G )
    local tbl;
    tbl:= OrdinaryCharacterTable( G );
    if HasSize( tbl ) then
      return Size( tbl );
    else
      TryNextMethod();
    fi;
    end );


#############################################################################
##
##  6. Attributes and Properties only for Character Tables
##

#############################################################################
##
#M  OrdersClassRepresentatives( <ordtbl> )  . for an ordinary character table
#M  OrdersClassRepresentatives( <modtbl> )  . .  for a Brauer character table
##
##  We delegate from <tbl> to the underlying group in the general case.
##  If we know the class lengths, however, we use them.
##
InstallMethod( OrdersClassRepresentatives,
    "for a Brauer character table (delegate to the ordinary table)",
    [ IsBrauerTable ],
    function( modtbl )
    local ordtbl;
    ordtbl:= OrdinaryCharacterTable( modtbl );
    return OrdersClassRepresentatives( ordtbl ){ GetFusionMap( modtbl,
               ordtbl ) };
    end );

InstallMethod( OrdersClassRepresentatives,
    "for a character table with known group",
    [ IsNearlyCharacterTable and HasUnderlyingGroup ],
    tbl -> List( ConjugacyClasses( tbl ),
                 c -> Order( Representative( c ) ) ) );

InstallMethod( OrdersClassRepresentatives,
    "for a character table, use known power maps",
    [ IsNearlyCharacterTable ],
    function( tbl )

    local pow, ord, p;

    # Compute the orders as determined by the known power maps.
    pow:= ComputedPowerMaps( tbl );
    if IsEmpty( pow ) then
      return fail;
    fi;
    ord:= ElementOrdersPowerMap( pow );
    if ForAll( ord, IsInt ) then
      return ord;
    fi;

    # If these maps do not suffice, compute the missing power maps
    # and then try again.
    for p in PrimeDivisors( Size( tbl ) ) do
      PowerMap( tbl, p );
    od;
    ord:= ElementOrdersPowerMap( ComputedPowerMaps( tbl ) );
    Assert( 2, ForAll( ord, IsInt ),
            "computed power maps should determine element orders" );

    return ord;
    end );


#############################################################################
##
#M  SizesCentralizers( <ordtbl> ) . . . . . . for an ordinary character table
#M  SizesCentralizers( <modtbl> ) . . . . . . .  for a Brauer character table
##
##  If we know the class lengths or the irreducible characters,
##  we prefer them to using a perhaps known group.
##
InstallMethod( SizesCentralizers,
    "for a Brauer character table",
    [ IsBrauerTable ],
    function( modtbl )
    local ordtbl;
    ordtbl:= OrdinaryCharacterTable( modtbl );
    return SizesCentralizers( ordtbl ){ GetFusionMap( modtbl, ordtbl ) };
    end );

InstallMethod( SizesCentralizers,
    "for a character table",
    [ IsNearlyCharacterTable ],
    function( tbl )
    local classlengths, size;

    if HasSizesConjugacyClasses( tbl ) then
      classlengths:= SizesConjugacyClasses( tbl );
      size:= Sum( classlengths, 0 );
      return List( classlengths, s -> size / s );
    elif HasIrr( tbl ) then
      return Sum( List( Irr( tbl ),
                        x -> List( x, y -> y * ComplexConjugate( y ) ) ) );
    elif HasUnderlyingGroup( tbl ) then
      size:= Size( tbl );
      return List( ConjugacyClasses( tbl ), c -> size / Size( c ) );
    fi;

    # Give up.
    TryNextMethod();
    end );


#############################################################################
##
#M  SizesConjugacyClasses( <ordtbl> ) . . . . for an ordinary character table
#M  SizesConjugacyClasses( <modtbl> ) . . . . .  for a Brauer character table
##
##  If we know the centralizer orders or the irreducible characters,
##  we prefer them to using a perhaps known group.
##
InstallMethod( SizesConjugacyClasses,
    "for a Brauer character table",
    [ IsBrauerTable ],
    function( modtbl )
    local ordtbl;
    ordtbl:= OrdinaryCharacterTable( modtbl );
    return SizesConjugacyClasses( ordtbl ){ GetFusionMap( modtbl,
                                                          ordtbl ) };
    end );

InstallMethod( SizesConjugacyClasses,
    "for a character table ",
    [ IsNearlyCharacterTable ],
    function( tbl )
    local centsizes, size;

    if HasSizesCentralizers( tbl ) or HasIrr( tbl ) then
      centsizes:= SizesCentralizers( tbl );
      size:= centsizes[1];
      return List( centsizes, s -> size / s );
    elif HasUnderlyingGroup( tbl ) then
      return List( ConjugacyClasses( tbl ), Size );
    fi;

    # Give up.
    TryNextMethod();
    end );


#############################################################################
##
#M  AutomorphismsOfTable( <tbl> ) . . . . . . . . . . . for a character table
##
InstallMethod( AutomorphismsOfTable,
    "for a character table",
    [ IsCharacterTable ],
    tbl -> TableAutomorphisms( tbl, Irr( tbl ) ) );


#############################################################################
##
#M  AutomorphismsOfTable( <modtbl> )  . . . for Brauer table & good reduction
##
##  The automorphisms may be stored already on the ordinary table.
##
InstallMethod( AutomorphismsOfTable,
    "for a Brauer table in the case of good reduction",
    [ IsBrauerTable ],
    function( modtbl )
    if Size( modtbl ) mod UnderlyingCharacteristic( modtbl ) = 0 then
      TryNextMethod();
    else
      return AutomorphismsOfTable( OrdinaryCharacterTable( modtbl ) );
    fi;
    end );


#############################################################################
##
#M  ClassNames( <tbl> )  . . . . . . . . . . class names of a character table
#M  ClassNames( <tbl>, \"ATLAS\" ) . . . . . class names of a character table
##
InstallMethod( ClassNames,
    [ IsNearlyCharacterTable ],
    tbl -> ClassNames( tbl, "default" ) );

InstallMethod( ClassNames,
    [ IsNearlyCharacterTable, IsString ],
    function( tbl, string )

    local i,        # loop variable
          alpha,    # alphabet
          lalpha,   # length of the alphabet
          number,   # at position <i> the current number of
                    # classes of order <i>
          unknown,  # number of next unknown element order
          names,    # list of classnames, result
          name,     # local function returning right combination of letters
          orders;   # list of representative orders

    if LowercaseString( string ) = "atlas" then

      alpha:= [ "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" ];

      name:= function( n )
        local m;
        if n <= lalpha then
          return alpha[n];
        else
          m:= (n-1) mod lalpha + 1;
          n:= ( n - m ) / lalpha;
          return Concatenation( alpha[m], String( n ) );
        fi;
      end;

    else

      alpha:= [ "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" ];

      name:= function(n)
        local name;
        name:= "";
        while 0 < n do
          name:= Concatenation( alpha[ (n-1) mod lalpha + 1 ], name );
          n:= QuoInt( n-1, lalpha );
        od;
        return name;
      end;

    fi;

    lalpha:= Length( alpha );
    names:= [];

    if IsCharacterTable( tbl ) or HasOrdersClassRepresentatives( tbl ) then

      # A character table can be asked for representative orders,
      # also if they are not yet stored.
      orders:= OrdersClassRepresentatives( tbl );
      number:= [];
      unknown:= 1;
      for i in [ 1 .. NrConjugacyClasses( tbl ) ] do
        if IsInt( orders[i] ) then
          if not IsBound( number[ orders[i] ] ) then
            number[ orders[i] ]:= 1;
          fi;
          names[i]:= Concatenation( String( orders[i] ),
                                    name( number[ orders[i] ] ) );
          number[ orders[i] ]:= number[ orders[i] ] + 1;
        else
          names[i]:= Concatenation( "?", name( unknown ) );
          unknown:= unknown + 1;
        fi;
      od;

    else

      names[1]:= Concatenation( "1", alpha[1] );
      for i in [ 2 .. NrConjugacyClasses( tbl ) ] do
        names[i]:= Concatenation( "?", name( i-1 ) );
      od;

    fi;

    # Return the list of classnames.
    return names;
    end );


#############################################################################
##
#M  CharacterNames( <tbl> )  . . . . . . character names of a character table
##
InstallMethod( CharacterNames,
    [ IsNearlyCharacterTable ],
    tbl -> List( [ 1 .. NrConjugacyClasses( tbl ) ],
                 i -> Concatenation( "X.", String( i ) ) ) );


#############################################################################
##
#M  \.( <tbl>, <name> ) . . . . . . . . . position of a class with given name
##
##  If <name> is a class name of the character table <tbl> as computed by
##  `ClassNames', `<tbl>.<name>' is the position of the class with this name.
##
InstallMethod( \.,
    "for class names of a nearly character table",
    [ IsNearlyCharacterTable, IsInt ],
    function( tbl, name )
    local pos;
    name:= NameRNam( name );
    pos:= Position( ClassNames( tbl ), name );
    if pos = fail then
      TryNextMethod();
    else
      return pos;
    fi;
    end );

#############################################################################
##
#F  ColumnCharacterTable( <tbl>,<nr> )
##
InstallGlobalFunction(ColumnCharacterTable,function(T,n)
  return Irr(T){[1..Length(Irr(T))]}[n];
end);


#############################################################################
##
#M  ClassPositionsOfNormalSubgroups( <tbl> )
##
InstallMethod( ClassPositionsOfNormalSubgroups,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    function( tbl )
    local kernels,  # list of kernels of irreducible characters
          normal,   # list of normal subgroups, result
          ker1,     # loop variable
          ker2,     # loop variable
          inter;    # intersection of two kernels

    # Get the kernels of irreducible characters.
    kernels:= Set( Irr( tbl ), ClassPositionsOfKernel );

    # Form all possible intersections of the kernels.
    normal:= ShallowCopy( kernels );
    for ker1 in normal do
      for ker2 in kernels do
        inter:= Intersection( ker1, ker2 );
        if not inter in normal then
          Add( normal, inter );
        fi;
      od;
    od;

    # Sort the list of normal subgroups (first lexicographically,
    # then --stable sort-- according to length and thus inclusion).
    normal:= SSortedList( normal );
    SortBy( normal, Length );

    # Represent the lists as ranges if possible.
    # (It is not possible to do this earlier since the representation
    # as a range may get lost in the `Intersection' call.)
    for ker1 in normal do
--> --------------------

--> maximum size reached

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

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

                                                                                                                                                                                                                                                                                                                                                                                                     


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