Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/GAP/pkg/ctbllib/gap4/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 22.4.2025 mit Größe 190 kB image not shown  

Quelle  ctadmin.gi   Sprache: unbekannt

 
#############################################################################
##
#W  ctadmin.gi           GAP 4 package CTblLib                  Thomas Breuer
#W                                                               Ute Schiffer
##
##  This file contains the implementation part of the data of the GAP
##  character table library that is not automatically produced from the
##  library files.
##
##  1. Representations of library tables
##  2. Functions used in the library files
##  3. Functions to construct library tables
##  4. Functions used as `construction' component of library tables
##  5. Selection functions for the table library
##  6. Functions to produce tables in library format
##
##  Note that in all construction functions, the table under construction is
##  a plain record, *not* a table object.
##


#############################################################################
##
#M  InfoText( <libtbl> )
##
##  <#GAPDoc Label="InfoText_libtable">
##  <ManSection>
##  <Meth Name="InfoText" Arg="tbl"/>
##
##  <Description>
##  This method for library character tables returns an empty string
##  if no <Ref Attr="InfoText"/> value is stored on the table <A>tbl</A>.
##  <P/>
##  Without this method, it would be impossible to use <Ref Attr="InfoText"/>
##  in calls to <Ref Func="AllCharacterTableNames"/>,
##  as in the following example.
##  <P/>
##  <Example><![CDATA[
##  gap> AllCharacterTableNames( InfoText,
##  >        s -> PositionSublist( s, "tests:" ) <> fail );;
##  ]]></Example>
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
##  We cannot use 'InstallMethod' since there are two declarations for the
##  attribute 'InfoText', and the method matches both of them.
##
##  (Apparently a more general declaration has been added recently.
##  A clean solution would be to change 'DeclareAttributeSuppCT'
##  such that no declaration happens if the operation exists already.)
##
InstallOtherMethod( InfoText,
    "for character tables in the library, the default is an empty string",
    [ "IsCharacterTable and IsLibraryCharacterTableRep" ],
    tbl -> "" );


#############################################################################
##
#F  PParseBackwards( <string>, <format> )
##
BindGlobal( "PParseBackwards", function( string, format )
#T Remove this as soon as `gpisotyp' is available!
    local result, pos, j, pos2;

    # Scan the string backwards.
    result:= [];
    pos:= Length( string );
    for j in Reversed( format ) do
      if IsString( j ) then
        pos2:= pos - Length( j );
        if pos2 < 0 or string{ [ pos2+1 .. pos ] } <> j then
          return fail;
        fi;
      else
        pos2:= pos;
        while 0 < pos2 and j( string[ pos2 ] ) do
          pos2:= pos2-1;
        od;
      fi;
      if j = IsDigitChar then
        Add( result, Int( string{ [ pos2+1 .. pos ] } ) );
      else
        Add( result, string{ [ pos2+1 .. pos ] } );
      fi;
      pos:= pos2;
    od;
    if 0 < pos then
      return fail;
    fi;

    return Reversed( result );
    end );


#############################################################################
##
#F  CTblLib.ReadTbl( [<filename>, ]<id> ) . . . . read character tables files
##
##  This function is used to read data files of the character table library.
##  If the initial part of <filename> is one of `~/', `/' or `./' then we
##  interpret the name as *absolute*, and try to read the file analogous to
##  `Read'; otherwise we interpret the name as *relative* to the `data'
##  directory of the CTblLib package.
##
CTblLib.ReadTbl:= function( arg )
    local name, id, var, readIndent, found;

    if Length( arg ) = 1 then
      id:= arg[1];
      name:= Concatenation( id, ".tbl" );
    else
      name:= arg[1];
      id:= arg[2];
    fi;

    # `LIBTABLE.TABLEFILENAME' is used in `MOT', `ALF', `ALN'.
    LIBTABLE.TABLEFILENAME:= id;
    if not IsBound( LIBTABLE.( id ) ) then
      LIBTABLE.( id ):= rec();
    fi;

    # Make some variables available that are used in data files.
    # Save perhaps available user variables with these names.
    if 2 <= Length( name )
       and ( name[1] = '/' or name{ [ 1, 2 ] } in [ "~/", "./" ] ) then
      # `name' is an absolute filename.
      CTblLib.ALN:= NotifyNameOfCharacterTable;
    else
      # The names in ``official'' table files are already stored.
      CTblLib.ALN:= Ignore;
    fi;
    for var in [ "ACM", "ALF", "ALN", "ARC", "MBT", "MOT" ] do
      if IsBoundGlobal( var ) then
        if IsReadOnlyGlobal( var ) then
          MakeReadWriteGlobal( var );
          CTblLib.( Concatenation( "GlobalR_", var ) ):= ValueGlobal( var );
        else
          CTblLib.( Concatenation( "GlobalW_", var ) ):= ValueGlobal( var );
        fi;
        UnbindGlobal( var );
      fi;
      ASS_GVAR( var, CTblLib.( var ) );
    od;

    if 2 <= Length( name )
       and ( name[1] = '/' or name{ [ 1, 2 ] } in [ "~/", "./" ] ) then
      name:= UserHomeExpand( name );
      if GAPInfo.CommandLineOptions.D then
        readIndent:= SHALLOW_COPY_OBJ( READ_INDENT );
        APPEND_LIST_INTR( READ_INDENT, "  " );
        Print( "#I", READ_INDENT, "Read( \"", name, "\" )\n" );
      fi;
      found:= IsReadableFile( name ) = true and READ( name );
      if GAPInfo.CommandLineOptions.D then
        READ_INDENT:= readIndent;
        if found and READ_INDENT = "" then
          Print( "#I  Read( \"", name, "\" ) done\n" );
        fi;
      fi;
    else
      found:= RereadPackage( "ctbllib", Concatenation( "data/", name ) );
    fi;

    # Unbind the variables again that have been assigned above.
    for var in [ "ACM", "ALF", "ALN", "ARC", "MBT", "MOT" ] do
      UnbindGlobal( var );
      if IsBound( CTblLib.( Concatenation( "GlobalR_", var ) ) ) then
        BindGlobal( var, CTblLib.( Concatenation( "GlobalR_", var ) ) );
        Unbind( CTblLib.( Concatenation( "GlobalR_", var ) ) );
      elif IsBound( CTblLib.( Concatenation( "GlobalW_", var ) ) ) then
        ASS_GVAR( var, CTblLib.( Concatenation( "GlobalW_", var ) ) );
        Unbind( CTblLib.( Concatenation( "GlobalW_", var ) ) );
      fi;
    od;

    return found;
    end;


#############################################################################
##
#V  LIBTABLE
##
InstallFlushableValue( LIBTABLE, rec(
    LOADSTATUS    := rec(),
    TABLEFILENAME := "",
    clmelab       := [],
    clmexsp       := [],
  ) );


#############################################################################
##
#V  LIBLIST
##
##  <#GAPDoc Label="LIBLIST">
##  <ManSection>
##  <Var Name="LIBLIST"/>
##
##  <Description>
##  &GAP;'s knowledge about the ordinary character tables in the
##  &GAP; Character Table Library is given by several JSON format files
##  that get evaluated when the file <F>gap4/ctprimar.g</F>
##  (the <Q>primary file</Q> of the character table library) is read.
##  These files can be produced from the data files,
##  see Section <Ref Subsect="subsec:CTblLib data files"/>.
##  <P/>
##  The information is stored in the global variable <Ref Var="LIBLIST"/>,
##  which is a record with the following components.
##  <P/>
##  <List>
##  <Mark><C>firstnames</C></Mark>
##  <Item>
##    the list of
##    <Ref Func="Identifier" Label="for character tables" BookName="ref"/>
##    values of the ordinary tables,
##  </Item>
##  <Mark><C>files</C></Mark>
##  <Item>
##    the list of filenames containing the data of ordinary tables,
##  </Item>
##  <Mark><C>filenames</C></Mark>
##  <Item>
##    a list of positive integers, value <M>j</M> at position <M>i</M> means
##    that the table whose identifier is the <M>i</M>-th in the
##    <C>firstnames</C> list is contained in the <M>j</M>-th file of the
##    <C>files</C> component,
##  </Item>
##  <Mark><C>fusionsource</C></Mark>
##  <Item>
##    a list containing at position <M>i</M> the list of names of tables that
##    store a fusion into the table whose identifier is the <M>i</M>-th in
##    the <C>firstnames</C> list,
##  </Item>
##  <Mark><C>allnames</C></Mark>
##  <Item>
##    a list of all admissible names of ordinary library tables,
##  </Item>
##  <Mark><C>position</C></Mark>
##  <Item>
##    a list that stores at position <M>i</M> the position in
##    <C>firstnames</C> of the identifier of the table with the <M>i</M>-th
##    admissible name in <C>allnames</C>,
##  </Item>
##  <Mark><C>simpleinfo</C></Mark>
##  <Item>
##    a list of triples <M>[ m, name, a ]</M> describing
##    the tables of simple groups in the library;
##    <M>name</M> is the identifier of the table,
##    <M>m</M><C>.</C><M>name</M> and <M>name</M><C>.</C><M>a</M> are
##    admissible names for its Schur multiplier and automorphism group,
##    respectively, if these tables are available at all,
##  </Item>
##  <Mark><C>sporadicSimple</C></Mark>
##  <Item>
##    a list of identifiers of the tables of the <M>26</M> sporadic simple
##    groups, and
##  </Item>
##  <Mark><C>GENERIC</C></Mark>
##  <Item>
##    a record with information about generic tables
##    (see Section <Ref Sect="sec:generictables"/>).
##  </Item>
##  </List>
##  </Description>
##  </ManSection>
##  <#/GAPDoc>
##
BindGlobal( "LIBLIST", rec() );

# The information about TomLib tables will be read as soon as
# this package is available.
# We initialize the interface here because rereading 'gap4/ctprimar.g'
# shall not overwrite known values.
LIBLIST.TOM_TBL_INFO_VERSION:= "no information about tables of marks";
LIBLIST.TOM_TBL_INFO:= [ [], [] ];

ReadPackage( "ctbllib", "gap4/ctprimar.g" );


#############################################################################
##
#F  GALOIS( <chars>, <list> )
#F  TENSOR( <chars>, <list> )
#F  EvalChars( <chars> )
##
InstallGlobalFunction( GALOIS, function( chars, li )
    return List( chars[ li[1] ], x -> GaloisCyc( x, li[2] ) );
end );

InstallGlobalFunction( TENSOR, function( chars, list )
    local i, chi, psi, result;
    chi:= chars[ list[1] ];
    psi:= chars[ list[2] ];
    result:= [];
    for i in [ 1 .. Length( chi ) ] do result[i]:= chi[i] * psi[i]; od;
    return result;
end );

InstallGlobalFunction( EvalChars, function( chars )
    local i;
    for i in [ 1 .. Length( chars ) ] do
      if IsFunction( chars[i][1] ) then
        chars[i]:= chars[i][1]( chars, chars[i][2] );
      fi;
    od;
    return chars;
end );


#############################################################################
##
#F  CTblLib.ALF( <from>, <to>, <map>[, <text>, <spec>] )  add library fusions
##
CTblLib.ALF:= function( arg )
    local pos, fus, text;

    if CTblLib.ALN <> Ignore then

      # A file is read that does not belong to the official library.
      # Check that the names are valid.
      if not arg[1] in RecNames( LIBTABLE.( LIBTABLE.TABLEFILENAME ) ) then
        Error( "source `", arg[1], "' is not stored in `LIBTABLE.",
               LIBTABLE.TABLEFILENAME, "'" );
      fi;
      pos:= Position( LIBLIST.firstnames, arg[2] );
#T this is not sorted! (take LIBLIST.allnames instead, and use indirection)
      if pos = fail then
        Info( InfoWarning, 1,
              "destination `", arg[2], "' is not a valid first name" );
      else

        # Check whether there was already such a fusion.
        if not arg[1] in LIBLIST.fusionsource[ pos ] then

          # Store the fusion source.
          LIBLIST.fusionsource:= ShallowCopy( LIBLIST.fusionsource );
          LIBLIST.fusionsource[ pos ]:= MakeImmutable( Concatenation(
              LIBLIST.fusionsource[ pos ], [ arg[1] ] ) );
          MakeImmutable( LIBLIST.fusionsource );

        fi;

      fi;

    fi;

    fus:= rec( name:= arg[2], map:= arg[3] );
    if 4 <= Length( arg ) then
      text:= Concatenation( arg[4] );
      ConvertToStringRep( text );
      fus.text:= text;
    fi;
    if Length( arg ) = 5 then
      text:= arg[5];
      ConvertToStringRep( text );
      fus.specification:= text;
    fi;

    Add( LIBTABLE.( LIBTABLE.TABLEFILENAME ).( arg[1] ).ComputedClassFusions,
         fus );
    end;


#############################################################################
##
#F  CTblLib.ACM( <spec>, <dim>, <val> ) . . . . . . . . . add Clifford matrix
##
CTblLib.ACM:= function( spec, dim, val )
    spec:= LIBTABLE.( Concatenation( "clm", spec ) );
    if not IsBound( spec[ dim ] ) then
      spec[ dim ]:= [];
    fi;
    Add( spec[ dim ], val );
    end;


#############################################################################
##
#F  CTblLib.ARC( <name>, <comp>, <val> ) . . . add component of library table
##
CTblLib.ARC:= function( name, comp, val )
    LIBTABLE.( LIBTABLE.TABLEFILENAME ).( name ).( comp ):= val;
    end;


#############################################################################
##
#F  NotifyGroupInfoForCharacterTable( <id>, <value> )
##
##  This function shall be available also if the Browse package is not
##  available, and then do nothing.
##
BindGlobal( "NotifyGroupInfoForCharacterTable", function( id, value )
    local enum, r, list, pos, i;

    if id in CTblLib.Data.IdEnumerator.identifiers then
      enum:= CTblLib.Data.IdEnumerator;
    elif id in CTblLib.Data.IdEnumeratorExt.identifiers then
      enum:= CTblLib.Data.IdEnumeratorExt;
    else
      return false;
    fi;

    if not IsBound( enum.attributes.indiv ) then
      return false;
    fi;

    r:= enum.attributes.indiv;
    if not IsBound( r.data ) then
      r.data:= rec( automatic:= [], nonautomatic:= [] );
    fi;
    list:= r.data.nonautomatic;
    pos:= PositionSorted( list, [ id ] );
    if IsBound( list[ pos ] ) then
      if list[ pos ][1] = id then
        Add( list[ pos ][2], value );
      else
        for i in [ Length( list ), Length( list ) - 1 .. pos ] do
          list[ i+1 ]:= list[i];
        od;
        list[ pos ]:= [ id, [ value ] ];
      fi;
    else
      Add( list, [ id, [ value ] ] );
    fi;

    return true;
    end );


#############################################################################
##
#F  CTblLib.NotifyNameOfCharacterTable( <firstname>, <newnames>, <pos> )
##
##  This code does not perform any argument check,
##  and does not clean up components in 'LIBLIST'.
##
CTblLib.NotifyNameOfCharacterTable:= function( firstname, newnames, pos,
    allnames, position )
    local lower, pos2, name, j;

    lower:= List( newnames, LowercaseString );
    if ForAny( lower, x -> x in allnames ) then
      Error( "<newnames> must contain only new names" );
    fi;

    Append( allnames, lower );
    Append( position, List( lower, x -> pos ) );
    end;


#############################################################################
##
#F  NotifyNameOfCharacterTable( <firstname>, <newnames> )
##
##  notifies the new names in the list <newnames> for the library table with
##  first name <firstname>, if there is no other table yet for that some of
##  these names are admissible.
##
InstallGlobalFunction( NotifyNameOfCharacterTable,
    function( firstname, newnames )
    local pos, allnames, position;

    if not ( IsString( firstname )
             and IsList( newnames ) and ForAll( newnames, IsString ) ) then
      Error( "<firstname> and entries in list <newnames> must be strings" );
    elif ForAny( [ 1 .. Length( firstname ) - 2 ],
               x -> firstname{ [ x .. x+2 ] } = "mod" ) then
      Error( "Brauer tables must not have explicitly given `othernames'" );
    fi;

    pos:= Position( LIBLIST.firstnames, firstname );
#T this is not sorted! (take LIBLIST.allnames instead, and use indirection)
    if pos = fail then
      Error( "no GAP library table with first name `", firstname, "'" );
    fi;

    # Change `LIBLIST'.
    allnames:= ShallowCopy( LIBLIST.allnames );
    position:= ShallowCopy( LIBLIST.position );

    CTblLib.NotifyNameOfCharacterTable( firstname, newnames, pos,
        allnames, position );
    SortParallel( allnames, position );
    LIBLIST.allnames:= MakeImmutable( allnames );
    LIBLIST.position:= MakeImmutable( position );
end );


#############################################################################
##
#F  NotifyCharacterTables( <list> )
##
InstallGlobalFunction( NotifyCharacterTables,
    function( list )
    local firstnames, filenames, files, fusionsource, allnames, position,
          triple, firstname, filename, othernames, len;

    if not IsList( list ) then
      Error( "<list> must be a list of triples" );
    fi;

    firstnames:= ShallowCopy( LIBLIST.firstnames );
    filenames:= ShallowCopy( LIBLIST.filenames );
    files:= ShallowCopy( LIBLIST.files );
    fusionsource:= ShallowCopy( LIBLIST.fusionsource );
    allnames:= ShallowCopy( LIBLIST.allnames );
    position:= ShallowCopy( LIBLIST.position );

    for triple in list do
      if Length( triple ) <> 3 then
        Error( "<list> must be a list of triples" );
      fi;

      firstname:= triple[1];
      filename:= triple[2];
      othernames:= triple[3];

      if not ( IsString( firstname ) and IsString( filename ) and
               IsList( othernames ) and ForAll( othernames, IsString ) ) then
        Error( "<firstname>, <filename> must be strings, ",
               "<othernames> must be a list of strings" );
      elif LowercaseString( firstname ) in LIBLIST.allnames then
        Error( "<firstname> is already a valid name" );
      fi;

      if not filename in files then
        Add( files, filename );
      fi;
      len:= Length( firstnames ) + 1;
      firstnames[ len ]:= firstname;
      filenames[ len ]:= Position( files, filename );
      fusionsource[ len ]:= [];

      CTblLib.NotifyNameOfCharacterTable( firstname, [ firstname ], len,
          allnames, position );
      CTblLib.NotifyNameOfCharacterTable( firstname, othernames, len,
          allnames, position );

      if IsBound( CTblLib.Data.IdEnumeratorExt.identifiers ) then
        Add( CTblLib.Data.IdEnumeratorExt.identifiers, firstname );

        # This is an ugly hack in order to achieve a better
        # integration of the SpinSym package.
        # It would be better (and cheaper) if the attributes would be set
        # inside the SpinSym package.
        # Also, we *want* those SpinSym tables that are available also in the
        # main library to be regarded as duplicates, although they are
        # formally not declared as duplicates, i. e., as permuted tables
        # of library tables.
        if IsBound( GAPInfo.PackageCurrent ) and
           IsBound( GAPInfo.PackageCurrent.PackageName ) and
           GAPInfo.PackageCurrent.PackageName = "SpinSym" then
          Add( CTblLib.SpinSymNames, firstname );
          if not IsBound( GAPInfo.PackageExtensionsLoaded ) then
            # We are in GAP up to version 4.12.
            # The SpinSym package notifies its tables
            # *after* the CTblLib package has been loaded.
            # Thus we have to set the attributes here.
            CTblLib.SetAttributesForSpinSymTable( firstname );
          fi;
        fi;
      fi;
    od;

    LIBLIST.firstnames:= MakeImmutable( firstnames );
    LIBLIST.filenames:= MakeImmutable( filenames );
    LIBLIST.files:= MakeImmutable( files );
    LIBLIST.fusionsource:= MakeImmutable( fusionsource );
    SortParallel( allnames, position );
    LIBLIST.allnames:= MakeImmutable( allnames );
    LIBLIST.position:= MakeImmutable( position );
end );


#############################################################################
##
#F  NotifyCharacterTable( <firstname>, <filename>, <othernames> )
##
InstallGlobalFunction( NotifyCharacterTable,
    function( firstname, filename, othernames )
    NotifyCharacterTables( [ [ firstname, filename, othernames ] ] );
end );


#############################################################################
##
#F  NotifyBrauerTable( <firstordname>, <p>, <filename> )
#F  NotifyBrauerTables( <list> )
##
BindGlobal( "NotifyBrauerTables", function( list )
    local firstmodnames, filenames, triple, firstordname, p, filename,
          lowername, pos;

    if not IsList( list ) then
      Error( "<list> must be a list of triples" );
    fi;

    firstmodnames:= ShallowCopy( LIBLIST.PrivateBrauerTables[1] );
    filenames:= ShallowCopy( LIBLIST.PrivateBrauerTables[2] );

    for triple in list do
      if Length( triple ) <> 3 then
        Error( "<list> must be a list of triples" );
      fi;

      firstordname:= triple[1];
      p:= triple[2];
      filename:= triple[3];

      if not ( IsString( firstordname ) and IsString( filename ) and
               IsPrimeInt( p ) ) then
        Error( "<firstordname>, <filename> must be strings, ",
               "<p> must be a prime integer" );
      fi;

      lowername:= MakeImmutable(
                      Concatenation( LowercaseString( firstordname ),
                                     "mod", String( p ) ) );
      pos:= Position( firstmodnames, lowername );
      if pos <> fail then
        if filenames[ pos ] <>filename then
          Error( "<firstordname> is already notified with a different file" );
        fi;
      else
        Add( firstmodnames, lowername );
        Add( filenames, Immutable( filename ) );
      fi;
    od;

    LIBLIST.PrivateBrauerTables[1]:= firstmodnames;
    LIBLIST.PrivateBrauerTables[2]:= filenames;
    SortParallel( firstmodnames, filenames );
end );

BindGlobal( "NotifyBrauerTable", function( firstordname, p, filename )
    NotifyBrauerTables( [ [ firstordname, p, filename ] ] );
end );


#############################################################################
##
#F  CTblLib.MBT( <arg> )
##
CTblLib.MBT:= function( arg )
    local i, record;

    record:= rec(
                  InfoText                 := arg[ 3],
                  UnderlyingCharacteristic := arg[ 2],
                  block                    := arg[ 4],
                  defect                   := arg[ 5],
                  basicset                 := arg[ 6],
                  brauertree               := arg[ 7],
                  decinv                   := arg[ 8],
                  factorblocks             := arg[ 9],
                  AutomorphismsOfTable     := arg[10],
                  indicator                := arg[11]
                 );

    for i in RecNames( record ) do
      if record.(i) = 0 then
        Unbind( record.(i) );
      fi;
    od;
    if Length( arg ) = 12 then
      for i in RecNames( arg[12] ) do
        record.(i):= arg[12].(i);
      od;
    fi;
    LIBTABLE.( LIBTABLE.TABLEFILENAME ).(
                 Concatenation( arg[1], "mod", String( arg[2] ) ) ):= record;
    end;


#############################################################################
##
#F  CTblLib.MOT( <arg> )
##
CTblLib.MOT:= function( arg )
    local record, i, len;

    # Construct the record.
    record:= rec(
                  Identifier               := arg[1],
                  InfoText                 := arg[2],
                  UnderlyingCharacteristic := 0,
                  SizesCentralizers        := arg[3],
                  ComputedPowerMaps        := arg[4],
                  ComputedClassFusions     := [],
                  Irr                      := arg[5],
                  AutomorphismsOfTable     := arg[6]
                 );

    for i in [ "InfoText", "SizesCentralizers", "ComputedPowerMaps",
               "ComputedClassFusions", "Irr", "AutomorphismsOfTable" ] do
      if record.(i) = 0 then
        Unbind( record.(i) );
      fi;
    od;
    if IsBound( arg[7] ) and IsList( arg[7] ) then
      record.ConstructionInfoCharacterTable:= arg[7];
    fi;
    len:= Length( arg );
    if IsRecord( arg[ len ] ) then
      for i in RecNames( arg[ len ] ) do
        record.(i):= arg[ len ].(i);
      od;
    fi;

    # Store the table record.
    LIBTABLE.( LIBTABLE.TABLEFILENAME ).( arg[1] ):= record;
    end;


#############################################################################
##
#V  GEN_Q_P
##
#F  PrimeBase( <q> )
##
InstallFlushableValue( GEN_Q_P, [] );

InstallGlobalFunction( PrimeBase, function( q )
    if not IsBound( GEN_Q_P[q] ) then
      GEN_Q_P[q]:= FactorsInt( q )[1];
    fi;
    return GEN_Q_P[q];
end );


#############################################################################
##
#F  LibInfoCharacterTable( <tblname> )
##
InstallGlobalFunction( LibInfoCharacterTable, function( tblname )
    local i, ordinfo, str, pos, filename;

    if IsCharacterTable( tblname ) then
      tblname:= Identifier( tblname );
    fi;

    # Is `tblname' the name of a Brauer table,
    # i.e., does it have the structure `<ordname>mod<prime>' ?
    # If so, return `<firstordname>mod<prime>' where
    # `<firstordname> = LibInfoCharacterTable( <ordname> ).firstName'.

    tblname:= LowercaseString( tblname );
    for i in [ 1 .. Length( tblname ) - 2 ] do
      if tblname{ [ i .. i+2 ] } = "mod" then
        ordinfo:= LibInfoCharacterTable( tblname{ [ 1 .. i-1 ] } );
        if ordinfo <> fail then
          ordinfo.firstName:= Concatenation( ordinfo.firstName,
                                  tblname{ [ i .. Length( tblname ) ] } );
          ConvertToStringRep( ordinfo.firstName );
          str:= ordinfo.fileName;
          pos:= Position( LIBLIST.PrivateBrauerTables[1],
                          LowercaseString( ordinfo.firstName ) );
          if pos <> fail then
            ordinfo.fileName:= LIBLIST.PrivateBrauerTables[2][ pos ];
          elif '/' in str then
            pos:= Maximum( Filtered( [ 1 .. Length( str ) ],
                           i -> str[i] = '/' ) );
            filename:= str{ [ pos+1 .. Length( str ) ] };
            if 3 <= Length( filename ) and filename{ [ 1 .. 3 ] } = "cto" then
              filename[3]:= 'b';
            fi;
            ordinfo.fileName:= Concatenation( str{ [ 1 .. pos ] }, filename );
          else
            ordinfo.fileName:= ShallowCopy( str );
            if 3 <= Length( str ) and str{ [ 1 .. 3 ] } = "cto" then
              ordinfo.fileName[3]:= 'b';
            fi;
          fi;
          ConvertToStringRep( ordinfo.fileName );
        fi;
        return ordinfo;
      fi;
    od;

    # The name might belong to an ordinary table.
    pos:= Position( LIBLIST.allnames, tblname );
    if pos <> fail then
      pos:= LIBLIST.position[ pos ];
      if pos <> fail then
        return rec( firstName := LIBLIST.firstnames[ pos ],
                    fileName  := LIBLIST.files[
                                     LIBLIST.filenames[ pos ] ] );
      fi;
      return fail;
    fi;

    # The name might belong to a generic table.
    if tblname in LIBLIST.GENERIC.allnames then
      return rec( firstName := LIBLIST.GENERIC.firstnames[
                            Position( LIBLIST.GENERIC.allnames,
                                      tblname ) ],
                  fileName  := "ctgeneri" );
    fi;

    return fail;
end );


#############################################################################
##
#F  LibraryTables( <filename> )
##
InstallGlobalFunction( LibraryTables, function( filename )
    local suffix, file, pos;

    # Omit the initial path for the name of the component in `LIBTABLE'.
    suffix:= filename;
    pos:= Position( suffix, '/' );
    while pos <> fail do
      suffix:= suffix{ [ pos+1 .. Length( suffix ) ] };
      pos:= Position( suffix, '/' );
    od;

    if not IsBound( LIBTABLE.LOADSTATUS.( suffix ) )
       or LIBTABLE.LOADSTATUS.( suffix ) = "unloaded" then

      # It is necessary to read a library file.
      # First unload all files which are not `"userloaded"', except that
      # with the ordinary resp. Brauer tables corresponding to those in
      # the file `filename'
      if UserPreference( "CTblLib", "UnloadCTblLibFiles" ) then
        for file in RecNames( LIBTABLE.LOADSTATUS ) do
          if LIBTABLE.LOADSTATUS.( file ) <> "userloaded" and
             suffix{ [ 4 .. Length( suffix ) ] }
              <> file{ [ 4 .. Length( file ) ] } then
            LIBTABLE.( file ):= rec();
            LIBTABLE.LOADSTATUS.( file ):= "unloaded";
          fi;
        od;
      fi;

      LIBTABLE.( suffix ):= rec();

      # Try to read the file.
      if not CTblLib.ReadTbl( Concatenation( filename, ".tbl" ), suffix ) then
        Info( InfoCharacterTable, 1,
              "no file `", filename,
              ".tbl' in the GAP Character Table Library" );
        return fail;
      fi;

      # Reset the load status from `"userloaded"' to `"loaded"'.
      LIBTABLE.LOADSTATUS.( suffix ):= "loaded";

    fi;

    return LIBTABLE.( suffix );
end );


############################################################################
##
#V  CTblLib.SupportedGenericIdentifiers
##
##  Make generic identifiers admissible.
##
CTblLib.SupportedGenericIdentifiers:= [
    [ PParseBackwards, [ "c", IsDigitChar ],
                       "Cyclic", [ 2 ] ],
    [ PParseBackwards, [ "alt(", IsDigitChar, ")" ],
                       "Alternating", [ 2 ] ],
    [ PParseBackwards, [ "sym(", IsDigitChar, ")" ],
                       "Symmetric", [ 2 ] ],
    [ PParseBackwards, [ "dihedral(", IsDigitChar, ")" ],
                       "Dihedral", [ 2 ] ],
    [ PParseBackwards, [ "2.sym(", IsDigitChar, ")" ],
                       "DoubleCoverSymmetric", [ 2 ] ],
    [ PParseBackwards, [ "2.alt(", IsDigitChar, ")" ],
                       "DoubleCoverAlternating", [ 2 ] ],
    ];


#############################################################################
##
#F  CTblLib.TrySpecialization( <tblname> )
##
CTblLib.TrySpecialization:= function( tblname )
    local entry, scan;

    tblname:= LowercaseString( tblname );
    for entry in CTblLib.SupportedGenericIdentifiers do
      scan:= entry[1]( tblname, entry[2] );
      if scan <> fail then
        return CallFuncList( CharacterTableFromLibrary,
                   Concatenation( [ entry[3] ], scan{ entry[4] } ) );
      fi;
    od;
    return fail;
    end;


#############################################################################
##
#F  CharacterTableFromLibrary( <tblname> )
#F  CharacterTableFromLibrary( <series>, <param1>[, <param2>] )
##
InstallGlobalFunction( CharacterTableFromLibrary, function( arg )
    local tblname, firstname, filename, suffix, pos, librarytables, name,
          libtbl, fld, i, fus;

    if IsEmpty( arg ) or not IsString( arg[1] ) then

      Error( "usage: CharacterTableFromLibrary( <tblname> )\n",
             " resp. CharacterTableFromLibrary( <series>, <parameters> )" );

    elif Length( arg ) = 1 then

      # `CharacterTableFromLibrary( tblname )'
      tblname:= arg[1];
      firstname:= LibInfoCharacterTable( tblname );
      if firstname = fail then
        # Perhaps it is the identifier of a generic table.
        libtbl:= CTblLib.TrySpecialization( tblname );
        if libtbl <> fail then
          return libtbl;
        fi;
        Info( InfoCharacterTable, 1,
              "No library table with name `", tblname, "'" );
        return fail;
      fi;
      filename  := firstname.fileName;
      firstname := firstname.firstName;
      suffix:= filename;
      pos:= Position( suffix, '/' );
      while pos <> fail do
        suffix:= suffix{ [ pos+1 .. Length( suffix ) ] };
        pos:= Position( suffix, '/' );
      od;

      if 3 < Length( suffix ) and suffix{ [ 1 .. 3 ] } = "ctb" then

        # Brauer table, call `BrauerTable'
        # (First get the ordinary table.)
        name:= PartsBrauerTableName( firstname );
        return BrauerTable(
                   CharacterTableFromLibrary( name.ordname ),
                   name.prime );

      fi;

      # ordinary or generic table

      librarytables:= LibraryTables( filename );

      if    librarytables = fail
         or not IsBound( librarytables.( firstname ) ) then
        Info( InfoCharacterTable, 1,
              "No library table with name `", tblname, "'" );
        return fail;
      fi;

      libtbl:= librarytables.( firstname );

      # If the table has not yet been converted to an object,
      # we must do this now.
      if IsRecord( libtbl ) then

        # If the table is a generic table then simply return it.
        if IsBound( libtbl.isGenericTable )
           and libtbl.isGenericTable = true then
          return libtbl;
        fi;

        # Concatenate the lines of the `InfoText' component.
        if IsBound( libtbl.InfoText ) then
          libtbl.InfoText:= Concatenation( libtbl.InfoText );
          ConvertToStringRep( libtbl.InfoText );
        fi;

        # Store the fusion sources.
        pos:= Position( LIBLIST.firstnames, firstname );
#T this is not sorted! (take LIBLIST.allnames instead, and use indirection)
        libtbl.NamesOfFusionSources:=
            ShallowCopy( LIBLIST.fusionsource[ pos ] );

        # Evaluate characters encoded as `[GALOIS,[i,j]]'
        # or `[TENSOR,[i,j]]'.
        if IsBound( libtbl.projectives ) then
          fld:= libtbl.projectives;
          libtbl.ProjectivesInfo:= [];
          Unbind( libtbl.projectives );
          for i in [ 1, 3 .. Length( fld ) - 1 ] do
            Add( libtbl.ProjectivesInfo,
                 rec( name:= fld[i], chars:= EvalChars( fld[i+1] ) ) );
          od;
        fi;

        # Obey the construction component.
        if IsBound( libtbl.ConstructionInfoCharacterTable ) then
          if IsFunction( libtbl.ConstructionInfoCharacterTable ) then
#T for backwards compatibility, supported in Version 1.1.
            libtbl.ConstructionInfoCharacterTable( libtbl );
          else
            CallFuncList(
                ValueGlobal( libtbl.ConstructionInfoCharacterTable[1] ),
                Concatenation( [ libtbl ],
                    libtbl.ConstructionInfoCharacterTable{ [ 2 ..  Length(
                        libtbl.ConstructionInfoCharacterTable ) ] } ) );
          fi;
        fi;

        # initialize some components
        if     IsBound( libtbl.ComputedPowerMaps )
           and not IsEmpty( libtbl.ComputedPowerMaps )
           and not IsBound( libtbl.OrdersClassRepresentatives ) then
          libtbl.OrdersClassRepresentatives:=
                       ElementOrdersPowerMap( libtbl.ComputedPowerMaps );
          if not ForAll( libtbl.OrdersClassRepresentatives, IsPosInt ) then
            Info( InfoWarning, 1,
                  "representative orders of library table ", tblname,
                  " not uniquely determined" );
            Unbind( libtbl.OrdersClassRepresentatives );
          fi;
        fi;

        if IsBound( libtbl.AutomorphismsOfTable ) and
           IsList( libtbl.AutomorphismsOfTable ) then
          libtbl.AutomorphismsOfTable:= GroupByGenerators(
                     libtbl.AutomorphismsOfTable, () );
        fi;

        if IsBound( libtbl.maxes ) then
          libtbl.Maxes:= libtbl.maxes;
          Unbind( libtbl.maxes );
        fi;

        if IsBound( libtbl.tomfusion ) then
          if IsBound( libtbl.tomfusion.text ) then
            libtbl.tomfusion.text:= Concatenation( libtbl.tomfusion.text );
            ConvertToStringRep( libtbl.tomfusion.text );
          fi;
          libtbl.FusionToTom:= libtbl.tomfusion;
# For backwards compatibility, do not unbind the component.
          libtbl.tomidentifier:= libtbl.tomfusion.name;
        fi;

        if IsBound( libtbl.isSimple ) then
          libtbl.IsSimpleCharacterTable:= libtbl.isSimple;
          Unbind( libtbl.isSimple );
        fi;

        if IsBound( libtbl.extInfo ) then
          libtbl.ExtensionInfoCharacterTable:= libtbl.extInfo;
          Unbind( libtbl.extInfo );
        fi;

        if IsBound( libtbl.CAS ) then
          libtbl.CASInfo:= libtbl.CAS;
          Unbind( libtbl.CAS );
        fi;
        if IsBound( libtbl.CASInfo ) then
          # For tables constructed from others,
          # the value may be copied from an attribute value
          # and hence may be immutable.
#T mutability problem:
#T if the following comment signs are removed then GAP runs into an error!
#         if not IsMutable( libtbl.CASInfo ) then
            libtbl.CASInfo:= List( libtbl.CASInfo, ShallowCopy );
#         fi;
          for i in libtbl.CASInfo do
            if IsBound( i.text ) and ForAll( i.text, IsString ) then
              i.text:= Concatenation( i.text );
              ConvertToStringRep( i.text );
            fi;
          od;
        fi;

        # Evaluate characters encoded as `[GALOIS,[i,j]]', `[TENSOR,[i,j]]'.
        EvalChars( libtbl.Irr );

        # Make the table object, and store it for the next call.
        ConvertToLibraryCharacterTableNC( libtbl );
        librarytables.( firstname ):= libtbl;

      fi;

      # Return the library table.
      return libtbl;

    else

      if arg[1] = "Quaternionic" and Length( arg ) = 2
         and IsInt( arg[2] ) then
        return CharacterTableQuaternionic( arg[2] );

      elif arg[1] = "GL" and Length( arg ) = 3
           and IsInt( arg[2] ) and IsInt( arg[3] ) then

        # `CharacterTable( GL, 2, q )'
        if arg[2] = 2 then
          return CharacterTableSpecialized(
                     CharacterTableFromLibrary( "GL2" ), arg[3] );
        else
          Info( InfoCharacterTable, 1,
                "Table of GL(", arg[2], ",q) not yet implemented" );
          return fail;
        fi;

      elif arg[1] = "SL" and Length( arg ) = 3
           and IsInt( arg[2] ) and IsInt( arg[3] ) then

        # CharacterTable( SL, 2, q )
        if arg[2] = 2 then
          if arg[3] mod 2 = 0 then
            return CharacterTableSpecialized(
                       CharacterTableFromLibrary( "SL2even" ),
                       arg[3] );
          else
            return CharacterTableSpecialized(
                       CharacterTableFromLibrary( "SL2odd" ),
                       arg[3] );
          fi;
        else
          Info( InfoCharacterTable, 1,
                "Table of SL(", arg[2], ",q) not yet implemented" );
          return fail;
        fi;

      elif arg[1] = "PSL" and Length( arg ) = 3
           and IsInt( arg[2] ) and IsInt( arg[3] ) then

        # PSL( 2, q )
        if arg[2] = 2 then
          if arg[3] mod 2 = 0 then
            return CharacterTableSpecialized(
                       CharacterTableFromLibrary( "SL2even" ),
                       arg[3] );
          elif ( arg[3] - 1 ) mod 4 = 0 then
            return CharacterTableSpecialized(
                       CharacterTableFromLibrary( "PSL2even" ),
                       arg[3] );
          else
            return CharacterTableSpecialized(
                       CharacterTableFromLibrary( "PSL2odd" ),
                       arg[3] );
          fi;
        else
          Info( InfoCharacterTable, 1,
                "Table of PSL(", arg[2], ",q) not yet implemented" );
          return fail;
        fi;

      elif arg[1] = "GU" and Length( arg ) = 3
           and IsInt( arg[2] ) and IsInt( arg[3] ) then

        # GU( 3, q )
        if arg[2] = 3 then
          return CharacterTableSpecialized(
                     CharacterTableFromLibrary( "GU3" ), arg[3] );
        else
          Info( InfoCharacterTable, 1,
                "Table of GU(", arg[2], ",q) not yet implemented" );
          return fail;
        fi;

      elif arg[1] = "SU" and Length( arg ) = 3
           and IsInt( arg[2] ) and IsInt( arg[3] ) then

        # SU( 3, q )
        if arg[2] = 3 then
          return CharacterTableSpecialized(
                     CharacterTableFromLibrary( "SU3" ),
                     arg[3] );
        else
          Info( InfoCharacterTable, 1,
                "Table of SU(", arg[2], ",q) not yet implemented" );
          return fail;
        fi;

      elif arg[1] = "Suzuki" and Length( arg ) = 2
           and IsInt( arg[2] ) then
        if not PrimeDivisors( arg[2] ) = [ 2 ] then
          Info( InfoCharacterTable, 1,
                "CharacterTable(\"Suzuki\",q): q must be a power of 2");
          return fail;
        fi;
        return CharacterTableSpecialized(
                   CharacterTableFromLibrary( "Suzuki" ),
                   [ arg[2],
                     2^((Length(FactorsInt(arg[2]))+1)/2) ] );

      else
        return CharacterTableSpecialized(
                   CharacterTableFromLibrary( arg[1] ), arg[2] );
      fi;
    fi;
end );


#############################################################################
##
#F  PartsBrauerTableName( <modname> )
##
InstallGlobalFunction( PartsBrauerTableName, function( modname )
    local i, primestring, ordname, prime, digits;

    primestring:= 0;
    for i in [ 1 .. Length( modname ) - 2 ] do
      if modname{ [ i .. i + 2 ] } = "mod" then
        primestring:= modname{ [ i + 3 .. Length( modname ) ] };
        ordname:= modname{ [ 1 .. i-1 ] };
      fi;
    od;
    if primestring = 0 then
      Print( "#I PartsBrauerTableName: ", modname,
             " is no valid name\n",
             "#I      for a Brauer table\n" );
      return fail;
    fi;

    # Convert the string back to a number.
    digits:= "0123456789";
    primestring:= List( primestring, x -> Position( digits, x ) );
    if fail in primestring then
      Print( "#I PartsBrauerTableName: ", modname,
             " is no valid name\n",
             "#I      for a Brauer table\n" );
      return fail;
    fi;
    prime:= 0;
    for i in [ 1 .. Length( primestring ) ] do
      prime:= 10 * prime + ( primestring[i] - 1 );
    od;

    return rec( ordname:= ordname, prime:= prime );
end );


#############################################################################
##
#F  BasicSetBrauerTree( <brauertree> )
##
InstallGlobalFunction( BasicSetBrauerTree, function( brauertree )
    local i, degrees, basicset, edge, elm;

    brauertree:= Set( brauertree );
    basicset:= [];

    # degrees of the vertices
    degrees:= [];
    for edge in brauertree do
      for i in edge do
        if not IsBound( degrees[i] ) then
          degrees[i]:= 1;
        else
          degrees[i]:= degrees[i] + 1;
        fi;
      od;
    od;

    while brauertree <> [] do

      # take a vertex of degree 1, remove its edge, adjust `degrees'
      elm:= Position( degrees, 1 );
      AddSet( basicset, elm );
      edge:= First( brauertree, x -> elm in x );
      RemoveSet( brauertree, edge );
      for i in edge do
        degrees[i]:= degrees[i] - 1;
      od;
    od;

    return basicset;
end );


#############################################################################
##
#F  DecMatBrauerTree( <brauertree> )
##
InstallGlobalFunction( DecMatBrauerTree, function( brauertree )
    local i, j, max, decmat;

    max:= 1;
    for i in brauertree do
      max:= Maximum( max, Maximum(i) );
    od;
    decmat:= NullMat( max, Length( brauertree ) );
    for i in [ 1 .. Length( brauertree ) ] do
      for j in brauertree[i] do
        decmat[j][i]:= 1;
      od;
    od;
    return decmat;
end );


#############################################################################
##
#F  BrauerTree( <decmat> )
##
InstallGlobalFunction( BrauerTree, function( decmat )
    local i, j, brauertree, edge, len;

    if not ( IsMatrix( decmat )
             and ForAll( decmat, x -> ForAll( x, y -> y=0 or y=1 ) ) ) then
      Print( "#I BrauerTree: <decmat> is not decomposition matrix\n",
             "#I     of a block of cyclic defect\n");
      return fail;
    fi;

    if decmat = [ [ 1 ] ] then return []; fi;

    brauertree:= [];
    for i in [ 1 .. Length( decmat[1] ) ] do

      # find the entries 1 in column `i'
      edge:= [];
      for j in [ 1 .. Length( decmat ) ] do
        if decmat[j][i] = 1 then Add( edge, j ); fi;
      od;
      len:= Length( edge );

      # If `len = 2', we have an ordinary edge of the tree; else this may
      # concern an exceptional character.

      if len = 2 then
        Add( brauertree, edge );
      else
        if Length( Set( decmat{ edge } ) ) <= 2 then

          # all or all but one ordinary irreducibles restrict identically
          Add( brauertree, edge );

        else
          Print( "#I BrauerTree: <decmat> is not decomposition",
                 " matrix\n",
                 "#I     of a block of cyclic defect\n");
          return fail;
        fi;
      fi;
    od;
    return brauertree;
end );


#############################################################################
##
#F  BrauerTableFromLibrary( <ordtbl>, <prime> )
##
InstallGlobalFunction( BrauerTableFromLibrary, function( ordtbl, prime )
    local filename,      # name of the file containing the Brauer table
          fld,           # library tables of the whole library file
          libtbl,        # record with data of the desired table
          reg,           # Brauer table, result
          op,            # largest normal $p$-subgroup
          orders,        # representative orders in `ordtbl'
          nccl,          # no. of classes in `ordtbl'
          entry,         # loop over stored fusions
          fusion,        # one fusion map
          result_blocks,
          i, j,
          ord,
          pow,
          ordblocks,
          modblocks,
          defect,
          name,
          irreducibles,
          restricted,
          block,
          basicset,
          class,
          images,
          chi,
          gal,
          newimages,
          pos,
          im,
          decmat,
          brauertree,
          facttbl,
          mfacttbl,
          pbl,
          info,
          factinfo,
          ordchars,
          offset,
          decinv,
          suffix;

    # Get the library file of the Brauer table if possible.
    name:= Concatenation( Identifier( ordtbl ), "mod", String( prime ) );
    filename:= LibInfoCharacterTable( name );
    if IsRecord( filename ) then
      filename:= filename.fileName;
      fld:= LibraryTables( filename );
    else
      fld:= fail;
    fi;

    if fld = fail or not IsBound( fld.( name ) ) then

      # For p-solvable tables, prefer the generic method.
      if IsPSolvableCharacterTable( ordtbl, prime ) then
        return fail;
      fi;

      # Maybe we have to factor out a normal $p$-subgroup before
      # we find the table (name) in the library.
      op:= ClassPositionsOfPCore( ordtbl, prime );
      if Length( op ) = 1 then
        Info( InfoCharacterTable, 1,
              "No library table with name `", name, "'" );
        return fail;
      fi;

      orders:= OrdersClassRepresentatives( ordtbl );
      nccl:= NrConjugacyClasses( ordtbl );
      for entry in ComputedClassFusions( ordtbl ) do
        fusion:= entry.map;
        if Positions( fusion, 1 ) = op then

          # We found the ordinary factor for which the Brauer characters
          # are equal to the ones we need.
          facttbl:= CharacterTableFromLibrary( entry.name );
          if facttbl = fail then
            return fail;
          fi;
          mfacttbl:= BrauerTable( facttbl, prime );
          if mfacttbl = fail then
            return fail;
          fi;

          # Now we set up a *new* Brauer table since the ordinary table
          # as well as the blocks information for the factor group is
          # different from the one for the extension.
          reg:= CharacterTableRegular( ordtbl, prime );
          SetFilterObj( reg, IsLibraryCharacterTableRep );

          # Set the irreducibles.
          # Note that the ordering of classes is in general *not* the same,
          # so we must translate with the help of fusion maps.
          fusion:= CompositionMaps(
                    InverseMap( GetFusionMap( mfacttbl, facttbl ) ),
                    CompositionMaps( GetFusionMap( ordtbl, facttbl ),
                                     GetFusionMap( reg, ordtbl ) ) );
          SetIrr( reg, List( Irr( mfacttbl ),
              chi -> Character( reg,
                  ValuesOfClassFunction( chi ){ fusion } ) ) );

          # Set known attribute values that can be copied from `mfacttbl'.
          if HasAutomorphismsOfTable( mfacttbl ) then
            SetAutomorphismsOfTable( reg, AutomorphismsOfTable( mfacttbl )
                ^ Inverse( PermList( fusion ) ) );
          fi;
          if HasInfoText( mfacttbl ) then
            SetInfoText( reg, InfoText( mfacttbl ) );
          fi;
          if HasComputedIndicators( mfacttbl ) then
            SetComputedIndicators( reg, ComputedIndicators( mfacttbl ) );
          fi;

          # Return the table.
          return reg;

        fi;
      od;

      Info( InfoCharacterTable, 1,
            "No library table of the factor by O_p" );
      return fail;

    fi;

    libtbl:= fld.( name );

    # If the table was already constructed simply return it.
    if IsBrauerTable( libtbl ) then
      return libtbl;
    fi;

    # Otherwise we have to work.
    reg:= CharacterTableRegular( ordtbl, prime );
    SetFilterObj( reg, IsLibraryCharacterTableRep );

#T just a hack ...
    reg!.defect:= libtbl.defect;
    reg!.block:= libtbl.block;
    if IsBound( libtbl.decinv ) then
      reg!.decinv:= libtbl.decinv;
    fi;
    if IsBound( libtbl.basicset ) then
      reg!.basicset:= libtbl.basicset;
    fi;
    if IsBound( libtbl.brauertree ) then
      reg!.brauertree:= libtbl.brauertree;
    fi;
#T end of the hack ...

    # Concatenate the lines of the `InfoText' component if necessary.
    if   not IsBound( libtbl.InfoText ) then
      SetInfoText( reg, "(no info text)" );
    elif IsString( libtbl.InfoText ) then
      SetInfoText( reg, libtbl.InfoText );
    else
      SetInfoText( reg, Concatenation( libtbl.InfoText ) );
    fi;

    # If automorphisms are known (list of generators), convert to a group.
    if IsBound( libtbl.AutomorphismsOfTable ) then
      SetAutomorphismsOfTable( reg,
          GroupByGenerators( libtbl.AutomorphismsOfTable, () ) );
    fi;

    # Initialize some components.
    if not IsBound( libtbl.decinv ) then
      libtbl.decinv:= [];
    fi;

    block:= [];
    defect:= [];
    basicset:= [];
    brauertree:= [];
    decinv:= [];

    # If the distribution to blocks is stored on the table
    # then use it, otherwise compute it.
    ordblocks:= InverseMap( PrimeBlocks( ordtbl, prime ).block );

    # Get the blocks of factor groups if necessary;
    # `factorblocks' is a list of pairs containing the names of the
    # tables that hold the blocks and the offset of basic set characters.
    if IsBound( libtbl.factorblocks ) then

      suffix:= filename;
      pos:= Position( suffix, '/' );
      while pos <> fail do
        suffix:= suffix{ [ pos+1 .. Length( suffix ) ] };
        pos:= Position( suffix, '/' );
      od;

      for i in libtbl.factorblocks do
        facttbl:= Concatenation( i[1], "mod", String( prime ) );
        if IsBound( LIBTABLE.( suffix ).( facttbl ) ) then
          facttbl:= LIBTABLE.( suffix ).( facttbl );
        else
          # The factor table is in another file (hopefully a rare case),
          # or it is obtained from a construction.
          facttbl:= CharacterTableFromLibrary( i[1] ) mod prime;
        fi;
        if block = [] then
          offset:= 0;
        else
          offset:= Maximum( block ) + 1 - Minimum( facttbl!.block );
        fi;
        pos:= Length( defect );
        Append( defect, facttbl!.defect );
        Append( block, offset + facttbl!.block );
        for j in [ 1 .. Length( facttbl!.defect ) ] do
          if facttbl!.defect[j] <> 0 then
            if IsBound( facttbl!.decinv ) and
               IsBound( facttbl!.decinv[j] ) then
              if IsInt( facttbl!.decinv[j] ) then
                decinv[ pos + j ]:= facttbl!.decinv[ facttbl!.decinv[j] ];
              else
                decinv[ pos + j ]:= facttbl!.decinv[j];
              fi;
              brauertree[ pos + j ]:= fail;
              basicset[ pos + j ]:= i[2] + facttbl!.basicset[j];
            else
              if IsInt( facttbl!.brauertree[j] ) then
                brauertree[ pos + j ]:=
                    facttbl!.brauertree[ facttbl!.brauertree[j] ];
              else
                brauertree[ pos + j ]:= facttbl!.brauertree[j];
              fi;
              basicset[ pos + j ]:= ordblocks[ pos + j ]{
                            BasicSetBrauerTree( brauertree[ pos + j ] ) };
            fi;
          fi;
        od;
      od;

      reg!.factorblocks:= libtbl.factorblocks;
#T a hack? (make the stored component evaluable)

    fi;

    pos:= Length( defect );
    Append( defect, libtbl.defect );
    Append( block, libtbl.block );
    for j in [ 1 .. Length( libtbl.defect ) ] do
      if libtbl.defect[j] <> 0 then
        if IsBound( libtbl.decinv[j] ) then
          if IsInt( libtbl.decinv[j] ) then
            decinv[ pos + j ]:= libtbl.decinv[ libtbl.decinv[j] ];
          else
            decinv[ pos + j ]:= libtbl.decinv[j];
          fi;
          brauertree[ pos + j ]:= fail;
          basicset[ pos + j ]:= libtbl.basicset[j];
        else
          if IsInt( libtbl.brauertree[j] ) then
            brauertree[ pos + j ]:=
                libtbl.brauertree[ libtbl.brauertree[j] ];
          else
            brauertree[ pos + j ]:= libtbl.brauertree[j];
          fi;
          basicset[ pos + j ]:= ordblocks[ pos + j ]{
                            BasicSetBrauerTree( brauertree[ pos + j ] ) };
        fi;
      fi;
    od;

    # compute the blocks and the irreducibles of each block,
    # and assign them to the right positions;
    # assign the known decomposition matrices and Brauer trees;
    # ignore defect 0 blocks
    irreducibles:= [];
    restricted:= RestrictedClassFunctions( Irr( ordtbl ), reg );

    modblocks := InverseMap( block );
    result_blocks:= [];

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

      if IsInt( ordblocks[i] ) then ordblocks[i]:= [ ordblocks[i] ]; fi;
      if IsInt( modblocks[i] ) then modblocks[i]:= [ modblocks[i] ]; fi;

      if defect[i] = 0 then

        irreducibles[ modblocks[i][1] ]:= restricted[ ordblocks[i][1] ];
        decinv[i]:= [ [1] ];
        basicset[i]:= ordblocks[i];

      else

        if IsBound( basicset[i] ) then
          if IsBound( brauertree[i] ) and brauertree[i] <> fail then
            decinv[i]:= DecMatBrauerTree( brauertree[i]){
                             Filtered( [ 1 .. Length( ordblocks[i] ) ],
                                       x -> ordblocks[i][x] in basicset[i] )
                            }^(-1) ;
          fi;
          if IsBound( decinv[i] ) then
            irreducibles{ modblocks[i] }:=
                List( decinv[i] * List( restricted{ basicset[i] },
                                        ValuesOfClassFunction ),
                      vals -> Character( reg, vals ) );
          else
            Error( "at least one of the components <decinv>, <brauertree> ",
                   "must be bound at pos. ", i );
          fi;
        else
          Print( "#E BrauerTable: no basic set for block ", i, "\n" );
        fi;
      fi;

      result_blocks[i]:= rec( defect    := defect[i],
                              ordchars  := ordblocks[i],
                              modchars  := modblocks[i],
                              decinv    := decinv[i],
                              basicset  := basicset[i]   );
      if IsBound( brauertree[i] ) and brauertree[i] <> fail then
        result_blocks[i].brauertree:= brauertree[i];
      fi;

    od;

    # instead of calling `Immutable' for the entries in the loop ...
    MakeImmutable( ordblocks );
    MakeImmutable( modblocks );
    MakeImmutable( decinv );
    MakeImmutable( basicset );
    MakeImmutable( brauertree );

    SetBlocksInfo( reg, result_blocks );
    SetIrr( reg, irreducibles );

    if IsBound( libtbl.CharacterParameters ) then
      SetCharacterParameters( reg, libtbl.CharacterParameters );
    fi;

    # decode the `IrredInfo' value
    # (contains 2nd indicator if the prime is 2, else nothing)
    if IsBound( libtbl.indicator ) then
      SetComputedIndicators( reg, [ , libtbl.indicator ] );
    fi;

#T BAD HACK until incomplete tables disappeared ...
#T only file ctborth2 ...
    if IsBound( libtbl.warning ) then
      Print( "#W warning for table of `", Identifier( reg ), "':\n",
             libtbl.warning, "\n" );
    fi;

    # Store additional information.
    # for the moment just as components.
    for entry in [ "version", "date" ] do
      if IsBound( libtbl.( entry ) ) then
        reg!.( entry ):= libtbl.( entry );
      fi;
    od;
    for entry in [ "ClassInfo", "RootDatumInfo" ] do
      if IsBound( libtbl.( entry ) ) then
        Setter( ValueGlobal( entry ) )( reg, libtbl.( entry ) );
      fi;
    od;

    # Store the Brauer table for the next call.
    fld.( name ):= reg;

    # Return the Brauer table.
    return reg;
end );


#############################################################################
##
#M  BrauerTableOp( <tbl>, <p> ) . . . . . . . . . . <p>-modular library table
##
##  Let <tbl> be an ordinary character table from the GAP table library,
##  and <p> be a prime integer.
##  - If the <p>-modular Brauer table of <tbl> is stored in a library file
##    then this table is returned.
##  - If <tbl> is <p>-solvable then we delegate to the library method
##    that is based on the Fong-Swan theorem.
##  - If the construction information stored on <tbl> admits the construction
##    of the <p>-modular Brauer table then the result of this construction
##    is returned.
##  - Otherwise fall back to the generic method.
##
InstallMethod( BrauerTableOp,
    [ "IsOrdinaryTable and IsLibraryCharacterTableRep", "IsPosInt" ], SUM_FLAGS,
    function( tbl, p )
    local result, modtbl, info, t, orig, fus, rest, modtblMG, modtblGA,
          tblG2, tblG3, found, cand, tblG, modtblG, modtblG2, modtblG3,
          modtblH1, modtblG1, modtblH2, perm,
          ordfacttbls, modfacttbls, modfacttbl, ordtbl, proj, inv, ker, irr;

    result:= fail;
    modtbl:= BrauerTableFromLibrary( tbl, p );
    if modtbl <> fail then
      result:= modtbl;
    elif IsPSolvableCharacterTable( tbl, p ) then
      # The generic method is preferred to table constructions.
      TryNextMethod();
    elif HasConstructionInfoCharacterTable( tbl ) then
      info:= ConstructionInfoCharacterTable( tbl );
      if IsList( info ) and info[1] = "ConstructPermuted" then
        t:= CallFuncList( CharacterTableFromLibrary, info[2] );
        orig:= t mod p;
        if orig <> fail then
          result:= CharacterTableRegular( tbl, p );
          # Restrict the permutation (if given) to the `p'-regular classes.
          fus:= GetFusionMap( result, tbl );
          if IsBound( info[3] ) then
            fus:= OnTuples( fus, Inverse( info[3] ) );
          fi;
          rest:= CompositionMaps( InverseMap( GetFusionMap( orig, t ) ),
                     fus );
          SetIrr( result, List( Irr( orig ),
                        chi -> Character( result,
                                 ValuesOfClassFunction( chi ){ rest } ) ) );
          # Transfer 2-modular indicators if they are stored.
          if p = 2 and HasComputedIndicators( orig ) and
             IsBound( ComputedIndicators( orig )[2] ) then
            ComputedIndicators( result )[2]:= ComputedIndicators( orig )[2];
          fi;
          # Transfer character parameters if they are stored.
          if HasCharacterParameters( orig ) then
            SetCharacterParameters( result, CharacterParameters( orig ) );
          fi;
        fi;
      elif IsList( info ) and info[1] = "ConstructMGA" then
        modtblMG:= CharacterTable( info[2] ) mod p;
        modtblGA:= CharacterTable( info[3] ) mod p;
        if ForAll( [ modtblMG, modtblGA ], IsCharacterTable ) then
          result:= BrauerTableOfTypeMGA( modtblMG, modtblGA, tbl );
          if result <> fail then
            result:= result.table;
          fi;
        fi;
      elif IsList( info ) and info[1] = "ConstructGS3" then
        # The identifier of the table of the normal subgroup 'G'
        # does not occur in the construction parameters.
        # We know that the factor group modulo 'G' is a Frobenius group
        # such that 'info[2]' modulo 'G' is a cyclic Frobenius complement.
        tblG2:= CharacterTable( info[2] );
        tblG3:= CharacterTable( info[3] );
        if ForAll( [ tblG2, tblG3 ], IsCharacterTable ) then
          found:= false;
          for cand in Intersection( NamesOfFusionSources( tblG2 ),
                                    NamesOfFusionSources( tblG3 ) ) do
            tblG:= CharacterTable( cand );
            if IsPrimeInt( Size( tblG2 ) / Size( tblG ) ) then
              found:= true;
              break;
            fi;
          od;
          if found then
            modtblG:= tblG mod p;
            modtblG2:= tblG2 mod p;
            modtblG3:= tblG3 mod p;
            if ForAll( [ modtblG, modtblG2, modtblG3 ],
                       IsCharacterTable ) then
              result:= CharacterTableOfTypeGS3( modtblG, modtblG2, modtblG3,
                  tbl,
                  Concatenation( Identifier( tbl ), "mod", String( p ) ) );
              if Length( Irr( result.table ) ) =
                 Length( Irr( result.table )[1] ) then
                result:= result.table;
              else
                # Not all irreducibles have been determined.
                result:= fail;
              fi;
            fi;
          fi;
        fi;
      elif IsList( info ) and info[1] = "ConstructIndexTwoSubdirectProduct"
           then
        modtblH1:= CharacterTable( info[2] ) mod p;
        modtblG1:= CharacterTable( info[3] ) mod p;
        modtblH2:= CharacterTable( info[4] ) mod p;
        modtblG2:= CharacterTable( info[5] ) mod p;
        if not fail in [ modtblH1, modtblG1, modtblH2, modtblG2 ] then
          perm:= info[7];
          if HasClassPermutation( tbl ) then
            perm:= perm * ClassPermutation( tbl );
          fi;
          result:= CharacterTableOfIndexTwoSubdirectProduct(
                       modtblH1, modtblG1, modtblH2, modtblG2,
                       [ tbl, perm ] );
          if result <> fail then
            result:= result.table;
          fi;
        fi;
      elif IsList( info ) and info[1] = "ConstructV4G" then
        result:= fail;
        if Length( info ) = 2 then
          ordfacttbls:= List( info[2], CharacterTableFromLibrary );
          modfacttbls:= List( ordfacttbls, x -> x mod p );
          if not fail in modfacttbls then
            result:= BrauerTableOfTypeV4G( tbl, modfacttbls );
          fi;
        else
          modfacttbl:= CharacterTable( info[2] ) mod p;
          if modfacttbl <> fail then
            result:= CallFuncList( BrauerTableOfTypeV4G,
                         Concatenation( [ tbl, modfacttbl ],
                             info{ [ 3 .. Length( info ) ] } ) );
          fi;
        fi;
      elif IsList( info ) and info[1] = "ConstructFactor" then
        # If the kernel in question is the p-core then
        # we would run into an infinite recursion
        # if we try to compute the Brauer table for the big table,
        # because the big table would delegate to the factor modulo
        # its p-core.
        ordtbl:= CallFuncList( CharacterTableFromLibrary, info[2] );
        if ClassPositionsOfPCore( ordtbl, p ) = info[3] then
          modtbl:= fail;
        else
          modtbl:= ordtbl mod p;
        fi;
        if modtbl <> fail then
          result:= CharacterTableRegular( tbl, p );
          proj:= GetFusionMap( modtbl, result );
          if proj = fail then
            result:= fail;
          else
            # It may happen that the kernel contains a nontrivial p-part.
            proj:= ProjectionMap( proj );
            inv:= InverseMap( GetFusionMap( modtbl, ordtbl ) );
            ker:= Filtered( info[3], i -> IsBound( inv[i] ) );
            ker:= inv{ ker };
            irr:= List( Filtered( Irr( modtbl ),
                                  x -> IsSubset( ClassPositionsOfKernel( x ),
                                       ker ) ),
                        x -> Character( result, x{ proj } ) );
            SetIrr( result, irr );
          fi;
        fi;
      fi;
    fi;

    if result <> fail then
      SetFilterObj( result, IsLibraryCharacterTableRep );
      if HasClassParameters( tbl ) then
        SetClassParameters( result,
            ClassParameters( tbl ){ GetFusionMap( result, tbl ) } );
      fi;
      return result;
    fi;

    TryNextMethod();
    end );


#############################################################################
##
#M  BrauerTable( <tblname>, <p> )
##
InstallMethod( BrauerTable,
    "for a string (the name of the table), and a prime",
    [ "IsString", "IsInt" ],
    function( tblname, p )
    local tbl;

    if not IsPrimeInt( p ) then
      Error( "<p> must be a prime integer" );
    fi;
    tbl:= CharacterTable( tblname );
    if tbl <> fail then
      tbl:= BrauerTable( tbl, p );
    fi;
    return tbl;
    end );


#############################################################################
##
#F  CharacterTableSpecialized( <generic_table>, <q> )  . . . . specialise <q>
##
InstallGlobalFunction( CharacterTableSpecialized, function( gtab, q )
    local taf,         # record of the specialized table, result
          genclass,    #
          classparam,  #
          genchar,     #
          charparam,   #
          parm,        #
          i, k,        #
          class;       #

    # Check if the argument is valid.
    if not ( IsRecord( gtab ) and IsBound( gtab.isGenericTable ) ) then
      Error( "this is not a generic character table" );
--> --------------------

--> maximum size reached

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

[ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ]