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


Quelle  ctadmin.gi   Sprache: unbekannt

 
Spracherkennung für: .gi vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

#############################################################################
##
#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.101 Sekunden  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


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