Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/ctbllib/gap3/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 28.1.2023 mit Größe 121 kB image not shown  

Quelle  ctadmin.g   Sprache: unbekannt

 
rahmenlose Ansicht.g DruckansichtUnknown {[0] [0] [0]}zum Wurzelverzeichnis wechseln

#############################################################################
##
#W  ctadmin.g           GAP character table library             Thomas Breuer
#W                                                               Ute Schiffer
##
##  This file contains the data of the {\GAP} character table library that is
##  not automatically produced from the library files.
##


#############################################################################
##
TBLNAME:= Concatenation( List( PKGNAME,
              x -> Concatenation( x, "ctbllib/data/;" ) ) );


#############################################################################
##
#F  Immutable( <obj> )
#F  MakeImmutable( <obj> )
#F  MakeReadOnlyGlobal( <obj> )
#F  TestPackageAvailability( ... )
#F  IsPosInt( <n> )
#V  TOM_TBL_INFO
#F  ListWithIdenticalEntries( <len>, <entry> )
#F  BindGlobal
#F  ValueGlobal
##
##  These are used in `ctprimar.tbl' but not available in {\GAP}~3.4.
##
if not IsBound( IsEmpty ) then
  IsEmpty:= list -> ( list = [] );
fi;
if not IsBound( Immutable ) then
  Immutable:= x -> x;
fi;
if not IsBound( MakeImmutable ) then
  MakeImmutable:= Ignore;
fi;
if not IsBound( MakeReadOnlyGlobal ) then
  MakeReadOnlyGlobal:= Ignore;
fi;
if not IsBound( TestPackageAvailability ) then
  TestPackageAvailability:= function( arg ) return true; end;
fi;
if not IsBound( IsPackageMarkedForLoading ) then
  IsPackageMarkedForLoading:= function( arg ) return true; end;
fi;
if not IsBound( IsPosInt ) then
  IsPosInt:= ( n -> IsInt( n ) and 0 < n );
fi;
if not IsBound( TOM_TBL_INFO ) then
  TOM_TBL_INFO:= [];
fi;
if not IsBound( ListWithIdenticalEntries ) then
  ListWithIdenticalEntries:= function( len, entry )
      return List( [ 1 .. len ], i -> entry );
  end;
fi;
if not IsBound( DuplicateFreeList ) then
  DuplicateFreeList:= function( list )
    local l, i;
    l:= [];
    for i in list do
      if not i in l then
        Add( l, i );
      fi;
    od;
    return l;
  end;
fi;
if not IsBound( BindGlobal ) then
  BindGlobal:= function( varname, value )
    if   varname = "LIBLIST" then
      LIBLIST:= value;
    elif varname = "TOM_TBL_INFO" then
      TOM_TBL_INFO:= value;
    else
      Error( "BindGlobal is not fully available in GAP 3" );
    fi;
  end;
fi;
ConstructIndexTwoSubdirectProduct:= 0;
ConstructSubdirect:= 0;
ConstructPermuted:= 0;
ConstructAdjusted:= 0;
ConstructFactor:= 0;
ConstructWreathSymmetric:= 0;
if not IsBound( ValueGlobal ) then
  ValueGlobal:= function( varname )
    local constr, pos;
    constr:= [ "ConstructMGA", ConstructMixed,
               "ConstructMixed", ConstructMixed,
               "ConstructProj",  ConstructProj,
               "ConstructDirectProduct",  ConstructDirectProduct,
               "ConstructSubdirect",  ConstructSubdirect,
               "ConstructIndexTwoSubdirectProduct",
                   ConstructIndexTwoSubdirectProduct,
               "ConstructWreathSymmetric", ConstructWreathSymmetric,
               "ConstructIsoclinic",  ConstructIsoclinic,
               "ConstructV4G",  ConstructV4G,
               "ConstructGS3",  ConstructGS3,
               "ConstructPermuted",  ConstructPermuted,
               "ConstructAdjusted", ConstructAdjusted,
               "ConstructFactor",  ConstructFactor,
               "ConstructClifford",  ConstructClifford ];
    pos:= Position( constr, varname );
    if pos <> false then
      return constr[ pos+1 ];
    else
      Error( "ValueGlobal is not fully available in GAP 3" );
    fi;
  end;
fi;


#############################################################################
##
#V  CharTableDoubleCoverAlternating
#V  CharTableDoubleCoverSymmetric
##
##  These are used in `data/ctgeneri.tbl' but are not available in {\GAP}~3.
##
CharTableDoubleCoverAlternating := rec();
CharTableDoubleCoverSymmetric := rec();


#############################################################################
##
#F  Conductor( <obj> )
##
##  This is used in `data/ctgeneri.tbl'.
##
Conductor:= NofCyc;


#############################################################################
##
#V  GAP_4_SPECIALS
##
##  list of pairs whose first entries are the `identifier' values
##  of tables whose `construction' component would require {\GAP}~4 features,
##  and the second entries are the corresponding functions that do the same
##  in {\GAP}~3.
##
GAP_4_SPECIALS := [
[ "2.(2xF4(2)).2", function( tbl )
  local pi, irr, i, outer1, outer2, chi, j, adjustch, adjustcl, z;
  pi:= (2,3)(6,7)(10,11)(14,15)(18,19)(22,23)(28,29)(32,33)(40,41)(44,45)
       (48,49)(58,59)(62,63)(66,67)(70,71)(74,75)(78,79)(82,83)(86,87)(90,91)
       (96,97)(100,101)(110,111)(114,115)(118,119)(122,123)(126,127)(132,133)
       (136,137)(140,141)(144,145)(150,151)(158,159)(162,163)(166,167)(170,
       171)(174,175)(182,183)(186,187)(190,191)(196,197)(200,201)(204,205)
       (208,209)(212,213)(228,229)(234,235)(246,247)(254,255)(258,259)(264,
       265)(268,269)(272,273)(276,277)(280,281)(284,285)(288,289)(292,293)
       (296,297)(300,301);
  ConstructDirectProduct( tbl, [["2.F4(2).2"],["Cyclic",2]], pi, () );
  Unbind( tbl.orders );
  Unbind( tbl.fusions[ Length( tbl.fusions ) ] );
  Unbind( tbl.fusions[ Length( tbl.fusions ) ] );
  irr:= tbl.irreducibles;
  for i in [ 1 .. Length( irr ) ] do
    irr[i]:= ShallowCopy( irr[i] );
  od;
  outer1:= [215..302];
  outer2:= [3,4,7,8,11,12,15,16,19,20,23,24,26,29,30,33,34,36,38,41,42,45,46,
  49,50,52,54,56,59,60,63,64,67,68,71,72,75,76,79,80,83,84,87,88,91,92,94,97,
  98,101,102,104,106,108,111,112,115,116,119,120,123,124,127,128,130,133,134,
  137,138,141,142,145,146,148,151,152,154,156,159,160,163,164,167,168,171,172,
  175,176,178,180,183,184,187,188,191,192,194,197,198,201,202,205,206,209,210,
  213,214,216,218,220,222,224,226,229,230,232,235,236,238,240,242,244,247,248,
  250,252,255,256,259,260,262,265,266,269,270,273,274,277,278,281,282,285,286,
  289,290,293,294,297,298,301,302];
  i:= E(4);
  for chi in irr do
    if chi[1] = chi[2] then
      if chi[1] <> chi[2] or chi[1] <> chi[3] then
        for j in outer1 do
          chi[j]:= i * chi[j];
        od;
      fi;
    else
      for j in outer2 do
        chi[j]:= i * chi[j];
      od;
    fi;
  od;
  adjustch:= [183,184,185,186,191,192,193,194,195,196,197,198,199,200,201,202,
  209,210,211,212,217,218,219,220,223,224,225,226,237,238,239,240,265,266,267,
  268,271,272,273,274,275,276,277,278,287,288,289,290,291,292,293,294,295,296,
  297,298,299,300,301,302];
  adjustcl:=[227,228,229,230,233,234,235,236,245,246,247,248,253,254,255,256,
  257,258,259,260,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,
  278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,
  297,298,299,300,301,302];
  z:= E(8);
  for chi in irr{ adjustch{ [ 1, 3 .. 59 ] } } do
    for j in adjustcl do
      chi[j]:= z * chi[j];
    od;
  od;
  z:= E(8)^3;
  for chi in irr{ adjustch{ [ 2, 4 .. 60 ] } } do
    for j in adjustcl do
      chi[j]:= z * chi[j];
    od;
  od;
end ],
[ "C9Y3.3^5.U4(2)", function( tbl )
  local e, e8, e2, e7, chi, i;
  ConstructDirectProduct( tbl, [["Cyclic",3],["3.3^5.U4(2)"]] );
  Unbind( tbl.orders );
  Unbind( tbl.fusions[ Length( tbl.fusions ) ] );
  Unbind( tbl.fusions[ Length( tbl.fusions ) ] );
  for i in [ 1 .. Length( tbl.irreducibles ) ] do
    tbl.irreducibles[i]:= ShallowCopy( tbl.irreducibles[i] );
  od;
  e:= E(9);
  e8:= E(9)^8;
  e2:= E(9)^2;
  e7:= E(9)^7;
  for chi in tbl.irreducibles do
    if chi[2] = chi[1] * E(3) then
      for i in [ 86 .. 170 ] do
        chi[i]:= chi[i] * e8;
      od;
      for i in [ 171 .. 255 ] do
        chi[i]:= chi[i] * e7;
      od;
    elif chi[2] = chi[1] * E(3)^2 then
      for i in [ 86 .. 170 ] do
        chi[i]:= chi[i] * e;
      od;
      for i in [ 171 .. 255 ] do
        chi[i]:= chi[i] * e2;
      od;
    fi;
  od;
end ],
[ "Isoclinic(3.U3(8)x3)", function( tbl )
  local dp, aux;
  dp:= CharTableDirectProduct( CharTable( "3.U3(8)" ),
                               CharTable( "Cyclic", 9 ) );
  aux:= CharTableFactorGroup( dp, [ 1, 16, 22 ] );
  tbl.centralizers:= aux.centralizers;
  tbl.powermap:= aux.powermap;
  tbl.irreducibles:= aux.irreducibles;
end ],
[ "Isoclinic(U3(8).3_3x3)", function( tbl )
  local dp, aux;
  dp:= CharTableDirectProduct( CharTable( "U3(8).3_3" ),
                               CharTable( "Cyclic", 9 ) );
  aux:= CharTableNormalSubgroup( dp, Concatenation( [ 1, 4 .. 124 ],
            [128,131,134,138,141,144,146,149,152,156,159,162,164,167,170,174,
             177,180,182,185,188,192,195,198,200,203,206,210,213,216,218,221,
             224,228,231,234,236,239,242,246,249,252] ) );
  tbl.centralizers:= aux.centralizers;
  tbl.powermap:= aux.powermap;
  tbl.irreducibles:= aux.irreducibles;
end ],
];


#############################################################################
##
#F  IrreducibleCharactersOfIsoclinicGroup( <irr>, <center>, <outer>, <xpos> )
##
IrreducibleCharactersOfIsoclinicGroup:= function( irr, center, outer, xpos )
    local nonfaith, faith, irreds, root1, chi, values, root2;

    # Adjust faithful characters in outer classes.
    nonfaith:= [];
    faith:= [];
    irreds:= [];
    root1:= E(4);
    if Length( center ) = 1 then
      # The central subgroup has order two.
      for chi in irr do
        values:= chi;
        if values[ center[1] ] = values[1] then
          Add( nonfaith, values );
        else
          values:= ShallowCopy( values );
          values{ outer }:= root1 * values{ outer };
          Add( faith, values );
        fi;
        Add( irreds, values );
      od;
    else
      # The central subgroup has order four.
      root2:= E(8);
      for chi in irr do
        values:= chi;
        if ForAll( center, i -> values[i] = values[1] ) then
          Add( nonfaith, values );
        else
          values:= ShallowCopy( values );
          if ForAny( center, i -> values[i] = values[1] ) then
            values{ outer }:= root1 * values{ outer };
          elif values[ xpos ] / values[1] = root1 then
            values{ outer }:= root2 * values{ outer };
          else
            # If B is the matrix for g in G, the matrix for gz in H
            # depends on the character value of z^2 = x;
            # we have to choose the same square root for the whole character,
            # so the two possibilities differ just by the ordering of the two
            # extensions which we get.
            values{ outer }:= root2^-1 * values{ outer };
          fi;
          Add( faith, values );
        fi;
        Add( irreds, values );
      od;
    fi;

    return rec( all:= irreds, nonfaith:= nonfaith, faith:= faith );
    end;


#############################################################################
##
#F  CharTableIsoclinic( <tbl> )
#F  CharTableIsoclinic( <tbl>, <classes_of_normal_subgroup> )
#F  CharTableIsoclinic( <tbl>, <nsg>, <center> )
##
##  for table of groups $2.G.2$, the character table of the isoclinic group
##  (see ATLAS, Chapter 6, Section 7)
##
CharTableIsoclinic;

CharTableIsoclinic := function( arg )
    local i,           # 'E(4)'
          j,           # loop variable
          chi,         # one character
          orders,
          class,
          map,
          tbl,         # input table
          linear,      # linear characters of 'tbl'
          isoclinic,   # the isoclinic table, result
          center,      # nontrivial class(es) contained in the center
          nsg,         # index 2 subgroup
          outer,       # classes outside the index 2 subgroup
          images,
          factorfusion,
          reg,         # restriction to regular classes
          half, kernel, xpos, irreds, invfusion, k, ypos, old;

    # check the argument
    if not ( Length( arg ) in [ 1 .. 3 ] and IsCharTable( arg[1] ) )
       or ( Length( arg ) = 2 and not IsList( arg[2] ) ) then
      Error( "usage: CharTableIsoclinic( tbl[, classes_of_nsg] )");
    fi;

    # get the ordinary table if necessary
    if IsBound( arg[1].ordinary ) then
      tbl:= arg[1].ordinary;
    else
      tbl:= arg[1];
    fi;
    if not IsBound( tbl.powermap ) then
      tbl.powermap:= [];
    fi;

    # compute the isoclinic table of the ordinary table

    # Get the classes of the normal subgroup of index 2.
    if Length( arg ) = 1 then
      nsg:= false;
      center:= false;
    elif Length( arg ) = 2 then
      # The 2nd argument describes the normal subgroup of index 2
      # or the centre.
      if IsList( arg[2] ) and Sum( tbl.classes{ arg[2] } ) = tbl.size / 2 then
        nsg:= arg[2];
        center:= false;
      else
        nsg:= false;
        center:= arg[2];
      fi;
    else
      nsg:= arg[2];
      center:= arg[3];
      if IsInt( center ) then
        center:= [ center ];
      fi;
    fi;

    # Check `nsg'.
    if nsg = false then
      # Identify the unique normal subgroup of index 2.
      half:= tbl.size / 2;
      linear:= Filtered( tbl.irreducibles, x -> x[1] = 1 );
      kernel:= Filtered( List( linear, KernelChar ),
                         ker -> Sum( tbl.classes{ ker } ) = half );
    elif IsList( nsg ) and Sum( tbl.classes{ nsg } ) = tbl.size / 2 then
      kernel:= [ nsg ];
    else
      Error( "normal subgroup described by <nsg> must have index 2" );
    fi;

    # Check `center'.
    if center = false then
      # Get the unique central subgroup of order 2 in the normal subgroup.
      center:= Filtered( [ 1 .. Length( tbl.classes ) ],
                         i -> tbl.classes[i] = 1 and tbl.orders[i] = 2
                              and ForAny( kernel, n -> i in n ) );
      if Length( center ) <> 1 then
        Error( "central subgroup of order 2 not uniquely determined,\n",
               "use CharacterTableIsoclinic( <tbl>, <classes>, <center> )" );
      fi;
    elif IsPosInt( center ) then
      center:= [ center ];
    else
      center:= Difference( center, [ 1 ] );
    fi;

    # If there is more than one index 2 subgroup
    # and if there is a unique central subgroup $Z$ of order 2 or 4
    # then consider only those index 2 subgroups containing $Z$.
    if 1 < Length( kernel ) then
      kernel:= Filtered( kernel, ker -> IsSubset( ker, center ) );
    fi;
    if Length( kernel ) <> 1 then
      Error( "normal subgroup of index 2 not uniquely determined,\n",
             "use CharacterTableIsoclinic( <tbl>, <classes_of_nsg> )" );
    fi;
    nsg:= kernel[1];

    if not IsSubset( nsg, center ) then
      Error( "<center> must lie in <nsg>" );
    elif ForAny( center, i -> tbl.classes[i] <> 1 ) then
      Error( "<center> must be a list of positions of central classes" );
    elif Length( center ) = 1 then
      xpos:= center[1];
      if tbl.orders[ xpos ] <> 2 then
        Error( "<center> must list the classes of a central subgroup" );
      fi;
    elif Length( center ) = 3 and ForAny( center, i -> tbl.orders[i] = 4 ) then
      xpos:= First( center, i -> tbl.orders[i] = 4 );
    else
      Error( "the central subgroup must have order 2 or 4" );
    fi;

    # classes outside the normal subgroup
    outer:= Difference( [ 1 .. Length( tbl.classes ) ], nsg );

    # Adjust faithful characters in outer classes.
    irreds:= IrreducibleCharactersOfIsoclinicGroup( tbl.irreducibles, center,
                 outer, xpos );

    # make the record of the isoclinic table
    isoclinic:= rec(
                     identifier   := Concatenation( "Isoclinic(",
                                                    tbl.identifier, ")" ),
                     size         := tbl.size,
                     centralizers := Copy( tbl.centralizers ),
                     classes      := Copy( tbl.classes ),
                     orders       := Copy( tbl.orders ),
                     fusions      := [],
                     fusionsource := [],
                     powermap     := Copy( tbl.powermap ),
                     irreducibles := irreds.all,
                     operations   := CharTableOps               );

    isoclinic.order:= isoclinic.size;
    isoclinic.name:= isoclinic.identifier;

    # get the fusion map onto the factor group modulo the center
    factorfusion:= CollapsedMat( irreds.nonfaith, [] ).fusion;
    invfusion:= InverseMap( factorfusion );

    # adjust the power maps
    for j in [ 1 .. Length( isoclinic.powermap ) ] do
      if IsBound( isoclinic.powermap[j] ) then
        map:= isoclinic.powermap[j];

        # For $p \bmod |z| = 1$, the map remains unchanged,
        # since $g^p = h$ implies $(gz)^p = hz^p = hz$ then.
        # So we have to deal with the cases $p = 2$ and $p$ congruent
        # to the other odd positive integers up to $|z| - 1$.
        k:= j mod ( 2 * Length( center ) + 2 );
        if j = 2 then
          ypos:= xpos;
        elif k <> 1 then
          ypos:= Powmap( tbl.powermap, (k-1)/2, xpos );
        fi;

        if k <> 1 then
          for class in outer do
            old:= map[ class ];
            images:= invfusion[ factorfusion[ old ] ];
            if IsList( images ) then
              if Length( center ) = 1 then
                # The image is ``the other'' class.
                images:= Difference( images, [ old ] );
              else
                # It can happen that the class powers to itself.
                # Use the character values for the decision.
                images:= Filtered( images,
                         x -> ForAll( irreds.faith,
                                chi -> chi[ old ] = 0 or
                                chi[x] / chi[ old ] = chi[ ypos ] / chi[1] ) );
              fi;
              map[ class ]:= images[1];
              if j = 2 then
                isoclinic.orders[ class ]:= 2 * tbl.orders[ images[1] ];
              fi;
            fi;
          od;
        fi;
      fi;
    od;

    # if we want the isoclinic table of a Brauer table then
    # transfer the normal subgroup information to the regular classes,
    # and adjust the irreducibles

    if tbl <> arg[1] then

      reg:= CharTableRegular( isoclinic, arg[1].prime );
      factorfusion:= GetFusionMap( reg, isoclinic );
      reg.irreducibles:= Copy( arg[1].irreducibles );
      center:= Position( factorfusion, center );
      outer:= Filtered( [ 1 .. Length( reg.centralizers ) ],
                        x -> factorfusion[x] in outer );

      for chi in Filtered( reg.irreducibles,
                           x -> x[ center ] <> x[1] ) do
        for class in outer do
          chi[ class ]:= i * chi[ class ];
        od;
      od;
  
      isoclinic:= reg;

    fi;

    # adjust the table name
    isoclinic.identifier:= Concatenation( "Isoclinic(",
                                          arg[1].identifier, ")" );

    # return the result
    return isoclinic;
    end;


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


#############################################################################
##
#F  GALOIS( <chars>, <list> )
#F  TENSOR( <chars>, <list> )
##
##  are global variables used to store the library tables in compressed form.
##
##  The entry '[GALOIS,[<i>,<j>]]' in the 'irreducibles' or 'projectives'
##  component of a library table means the <j>-th Galois conjugate of
##  the <i>-th character.
##
##  The entry '[TENSOR,[<i>,<j>]]' in the 'irreducibles' or 'projectives'
##  component of a library table means the tensor product of the <i>-th
##  and the <j>-th character.
##
#F  EvalChars( <chars> )
##
##  replaces all entries of the form '[<func>,<list>]' in the list <chars>
##  by the result '<func>( <chars>, <list> )'.
##
GALOIS := function( chars, li )
    return List( chars[ li[1] ], x -> GaloisCyc( x, li[2] ) );
    end;

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;

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


#############################################################################
##
#F  MBT( <arg> )
##
##  The library format of Brauer tables is a call to the function
##  'MBT', with the following arguments.
##
##   1. identifier of the table
##   2. field characteristic
##   3. text (list of lines)
##   4. block
##   5. defect
##   6. basic set
##   7. Brauer tree information
##   8. inverses of decomposition matrices restricted to basic sets
##   9. blocks of proper factor groups
##  10. list of generators for the group of table automorphisms
##  11. 2nd indicator (in characteristic 2 only)
##  12. (optional) record with additional components
##
MBT := function( arg )
    local i, record;

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

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


#############################################################################
##
#F  MOT( <arg> )
##
##  The library format of ordinary character tables is a call to the function
##  'MOT', with the following arguments.
##
##   1. identifier of the table
##   2. text (list of lines)
##   3. list of centralizer orders
##   4. list of power maps
##   5. list of irreducibles
##   6. list of generators for the group of table automorphisms
##   7. (optional) construction of the table
##
##  Each fusion is added by 'ALF', any other component of the table must be
##  added individually via 'ARC( <identifier>, <compname>, <compval> )'.
##
##  'MOT' constructs a (preliminary) table record, and puts it into the
##  component 'LIBTABLE.TABLEFILENAME' of 'LIBTABLE'.
##  The 'fusionsource' and 'projections' are dealt with when the table is
##  constructed by 'CharTableLibrary'.
##  Admissible names are notified by 'ALN( <name>, <othernames> )'.
##
MOT := function( arg )
    local record, i;

    # Construct the table record.
    record:= rec(
                  text             := arg[2],
                  centralizers     := arg[3],
                  powermap         := arg[4],
                  fusions          := [],
                  irreducibles     := arg[5],
                  automorphisms    := arg[6]
                 );

    for i in RecFields( record ) do
      if record.(i) = 0 then
        Unbind( record.(i) );
      fi;
    od;
    if IsBound( arg[7] ) then
      record.construction:= arg[7];
    fi;

    # Store the table record.
    LIBTABLE.( LIBTABLE.TABLEFILENAME ).( arg[1] ):= record;
#Print( "stored for ", arg[1], " in ", LIBTABLE.TABLEFILENAME, "\n" );
    end;


#############################################################################
##
#F  LowercaseString( <string> ) . . . string consisting of lower case letters
##
LowercaseString := function( str )
    local alp, ALP, result, i, pos;

    alp:= "abcdefghijklmnopqrstuvwxyz";
    ALP:= "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    result:= "";
    for i in str do
      pos:= Position( ALP, i );
      if pos = false then
        Add( result, i );
      else
        Add( result, alp[ pos ] );
      fi;
    od;
    return result;
    end;


#############################################################################
##
#F  NotifyCharTableName( <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.
##
NotifyCharTableName := function( firstname, newnames )
    local lower,
          pos,
          pos2,
          name,
          j;

    if not ( IsString( firstname )
             and IsList( newnames ) and ForAll( newnames, IsString ) ) then
      Error( "<firstname> and entries in list <newnames> must be strings" );
    fi;
    if 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 );
    if pos = false then
      Error( "no GAP library table with first name '", firstname, "'" );
    fi;
    lower:= List( newnames, LowercaseString );
    if ForAny( lower, x -> x in LIBLIST.allnames ) then
      Error( "<newnames> must contain only new names" );
    fi;
    Append( LIBLIST.allnames, lower );
    Append( LIBLIST.position, List( lower, x -> pos ) );
    SortParallel( LIBLIST.allnames, LIBLIST.position );
    end;


#############################################################################
##
#F  NotifyCharTable( <firstname>, <filename>, <othernames> )
##
##  notifies a new ordinary table to the library.
##  This table has 'identifier' component <firstname>,
##  it is contained in the file with name <filename>, and
##  it is known to have also the names contained in the list <othernames>.
##
##  'NotifyCharTable' modifies the global variable 'LIBLIST' after having
##  checked that there is no other table yet with admissible name equal to
##  <firstname> or contained in <othernames>.
##
NotifyCharTable := function( firstname, filename, othernames )
    local len, pos;

    if not ( IsString( firstname ) and IsString( filename )
                                   and IsList( othernames ) ) then
      Error( "<firstname>, <filename> must be strings, ",
             "<othernames> must be a list" );
    fi;
    if LowercaseString( firstname ) in LIBLIST.allnames then
      Error( "'", firstname, "' is already a valid name" );
    fi;
    Add( LIBLIST.firstnames, firstname );
    if not filename in LIBLIST.files then
      Add( LIBLIST.files, filename );
    fi;
    len:= Length( LIBLIST.firstnames );
    LIBLIST.filenames[ len ]:= Position( LIBLIST.files, filename );
    LIBLIST.fusionsource[ len ]:= [];
    NotifyCharTableName( firstname, [ firstname ] );
    NotifyCharTableName( firstname, othernames );

    # Allow natural names.
#T !!
end;


#############################################################################
##
#F  LibInfoCharTable( <tblname> )
##
##  is a record with components 'firstName' and 'fileName', the former being
##  the 'identifier' component of the library table for that <tblname> is an
##  admissible name, and the latter being the name of the file in that the
##  table is stored;
##  if no such table exists in the {\GAP} library then 'false' is returned.
##
##  If <tblname> contains the substring "mod" it is regarded as name of a
##  Brauer table, the first name is computed from that of the corresponding
##  ordinary table (which must exist) also if the library does not contain
##  the Brauer table.
##
LibInfoCharTable := function( tblname )
    local i, ordinfo, obj, pos;

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

    tblname:= LowercaseString( tblname );
    for i in [ 1 .. Length( tblname ) - 2 ] do
      if tblname{ [ i .. i+2 ] } = "mod" then
        ordinfo:= LibInfoCharTable( tblname{ [ 1 .. i-1 ] } );
        if ordinfo <> false then
          Append( ordinfo.firstName, tblname{ [ i .. Length( tblname ) ] } );
          ordinfo.fileName[3]:= 'b';
        fi;
        return ordinfo;
      fi;
    od;

    # The name might belong to an ordinary table.
    pos:= PositionSorted( LIBLIST.allnames, tblname );
    if Length( LIBLIST.allnames ) < pos or
       LIBLIST.allnames[ pos ] <> tblname then
      pos:= false;
    fi;
    if pos <> false then
      pos:= LIBLIST.position[ pos ];
      if pos <> false then
        return rec( firstName := Copy( LIBLIST.firstnames[ pos ] ),
                    fileName  := Copy( LIBLIST.files[
                                             LIBLIST.filenames[ pos ] ] ) );
      fi;
      return false;
    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 false;
end;


#############################################################################
##
#F  FirstNameCharTable( <tblname> )
#F  FileNameCharTable( <tblname> )
##
FirstNameCharTable := function( name )
    name:= LibInfoCharTable( name );
    if name <> false then
      name:= name.firstName;
    fi;
    return name;
end;

FileNameCharTable := function( name )
    name:= LibInfoCharTable( name );
    if name <> false then
      name:= name.fileName;
    fi;
    return name;
end;


#############################################################################
##
#F  ALN( <name>, <names> )  . . . . . . . . . . . . . add library table names
##
ALN := NotifyCharTableName;


#############################################################################
##
#F  ALF( <from>, <to>, <map> ) . . . . . . . . . .  add library table fusions
#F  ALF( <from>, <to>, <map>, <text> )
##
ALF := function( arg )
    local pos;

    if ALN <> Ignore then

      # A file is read that does not belong to the official library.
      # Check that the names are valid.
      pos:= Position( LIBLIST.firstnames, arg[2] );
      if not arg[1] in RecFields( LIBTABLE.( LIBTABLE.TABLEFILENAME ) ) then
        Error( "source '", arg[1], "' is not stored in 'LIBTABLE.",
               LIBTABLE.TABLEFILENAME, "'" );
      elif pos = false then
        Error( "destination '", arg[2], "' is not a valid first name" );
      fi;

      # Check whether there was already such a fusion.
      if arg[1] in LIBLIST.fusionsource[ pos ] then
        Error( "there is already a fusion from '",
               arg[1], "' to '", arg[2], "'" );
      fi;

      # Store the fusion source.
      Add( LIBLIST.fusionsource[ pos ], arg[1] );

    fi;

    if Length( arg ) = 4 then
      Add( LIBTABLE.( LIBTABLE.TABLEFILENAME ).( arg[1] ).fusions,
           rec( name:= arg[2], map:= arg[3],
                text:= Concatenation( arg[4] ) ) );
    else
      Add( LIBTABLE.( LIBTABLE.TABLEFILENAME ).( arg[1] ).fusions,
           rec( name:= arg[2], map:= arg[3] ) );
    fi;
end;


#############################################################################
##
#F  ACM( <spec>, <dim>, <val> ) . . . . . . . . . . . . . add Clifford matrix
##
##  <spec> is one of "elab", "exsp".
##  <dim> is the dimension of the Clifford matrix,
##  <val> is the Clifford matrix itself.
##
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  ARC( <name>, <comp>, <val> ) . . . . . . . add component of library table
##
ARC := function( name, comp, val )
    local r;

    if comp = "CAS" then
      for r in val do
        if IsBound( r.text ) and not IsString( r.text ) then
          r.text:= Concatenation( r.text );
        fi;
      od;
    fi;
    LIBTABLE.( LIBTABLE.TABLEFILENAME ).( name ).( comp ):= val;
end;


#############################################################################
##
#F  ConstructMixed( <tbl>, <subname>, <factname>, <plan>, <perm> )
##
##  <tbl> is the table of a group $m.G.a$,
##  <subname> is the name of a subgroup $m.G$ which is a cyclic central
##  extension of the (not necessarily simple) group $G$,
##  <factname> is the name of the factor group $G.a$ of <tbl> where the
##  outer automorphisms $a$ (a group of prime order) acts nontrivially on
##  the central $m$.
##  Then the faithful characters of <tbl> are induced characters of $m.G$.
##
##  <plan> is a list of lists, each containing the numbers of characters of
##  $m.G$ that form an orbit under the action of $a$
##  (so the induction of characters is simulated).
##  <perm> is the permutation that must be applied to the list of characters
##  that is obtained on appending the faithful characters to the
##  inflated characters of the factor group.
##
##  Examples of tables where this is used to compress the library files are
##  the tables of $3.F_{3+}.2$ (subgroup $3.F_{3+}$, factor group $F_{3+}.2$)
##  and $6.Fi_{22}.2$ (subgroup $6.Fi_{22}$, factor group $2.Fi_{22}.2$).
##
ConstructMixed := function( tbl, sub, fact, plan, perm )
    local factfus,  # factor fusion from 'tbl' to 'fact'
          subfus,   # subgroup fusion from 'sub' to 'tbl'
          proj,     # projection map of 'subfus'
          irreds,   # list of irreducibles
          zero,     # list of zeros to be appended to the characters
          irr, newirr, entry, sum, chi, i;

    fact    := CharTable( fact );
    sub     := CharTable( sub  );
    factfus := GetFusionMap( tbl, fact );
    subfus  := GetFusionMap( sub, tbl );
    proj    := ProjectionMap( subfus );
    irreds  := List( fact.irreducibles, x -> x{ factfus } );
    zero    := [ 1 .. Length( factfus ) ] * 0;
    irr     := sub.irreducibles;
    newirr  := [];
    for entry in plan do
      # Note that `proj' need not be dense.
      sum:= Sum( irr{ entry } );
      chi:= ShallowCopy( zero );
      for i in [ 1 .. Length( chi ) ] do
        if IsBound( proj[i] ) then
          chi[i]:= sum[ proj[i] ];
        fi;
      od;
      Add( newirr, chi );
    od;
    Append( irreds, newirr );
    tbl.irreducibles:= Permuted( irreds, perm );
    end;


#############################################################################
##
#F  ConstructProj( <tbl>, <irrinfo> )
##
##  constructs irreducibles for projective tables from projectives of
##  a factor group table.
##
ConstructProj := function( tbl, irrinfo )
    local i, j, factor, fus, mult, irreds, linear, omegasquare, I,
          d, name, factfus, proj, adjust, Adjust,
          ext, lin, chi, faith, nccl, partner, divs, prox, foll,
          vals;

    nccl:= Length( tbl.centralizers );
    factor:= CharTable( irrinfo[1][1] );
    fus:= GetFusionMap( tbl, factor );
    mult:= tbl.centralizers[1] / factor.centralizers[1];
    irreds:= List( factor.irreducibles, x -> x{ fus } );
    linear:= Filtered( irreds, x -> x[1] = 1 );
    linear:= Filtered( linear, x -> ForAny( x, y -> y <> 1 ) );

    # some roots of unity
    omegasquare:= E(3)^2;
    I:= E(4);

    # Loop over the divisors of 'mult' (a divisor of 12).
    # Note the succession for 'mult = 12'!
    if mult <> 12 then
      divs:= Difference( DivisorsInt( mult ), [ 1 ] );
    else
      divs:= [ 2, 4, 3, 6, 12 ];
    fi;

    for d in divs do

      # Construct the faithful irreducibles for an extension by 'd'.
      # For that, we split and adjust the portion of characters (stored
      # on the small table 'factor') as if we would create this extension,
      # and then we blow up these characters to the whole table.

      name:= irrinfo[d][1];
      partner:= irrinfo[d][2];
      proj:= First( factor.projectives, x -> x.name = name );
      faith:= List( proj.chars, y -> y{ fus } );
      proj:= Copy( proj.map );

      if name = tbl.identifier then
        factfus:= [ 1 .. Length( tbl.centralizers ) ];
      else
        factfus:= First( tbl.fusions, x -> x.name = name ).map;
      fi;
      Add( proj, Length( factfus ) + 1 );    # for termination of loop
      adjust:= [];
      for i in [ 1 .. Length( proj ) - 1 ] do
        for j in [ proj[i] .. proj[i+1]-1 ] do
          adjust[ j ]:= proj[i];
        od;
      od;

      # now we have to multiply the values on certain classes 'j' with
      # roots of unity, dependent on the value of 'd'\:

      Adjust:= [];
      for i in [ 1 .. d-1 ] do
        Adjust[i]:= Filtered( [ 1 .. Length( factfus ) ],
                              x -> adjust[ factfus[x] ] = factfus[x] - i );
      od;

      # d =  2\:\ classes in 'Adjust[1]' multiply with '-1'
      # d =  3\:\ classes in 'Adjust[x]' multiply
      #                       with 'E(3)^x' for the proxy cohort,
      #                       with 'E(3)^(2*x)' for the follower cohort
      # d =  4\:\ classes in 'Adjust[x]' multiply
      #                       with 'E(4)^x' for the proxy cohort,
      #                       with '(-E(4))^x' for the follower cohort,
      # d =  6\:\ classes in 'Adjust[x]' multiply with '(-E(3))^x'
      # d = 12\:\ classes in 'Adjust[x]' multiply with '(E(12)^7)^x'
      #
      # (*Note* that follower cohorts of classes never occur in projective
      #  ATLAS tables ... )

      # determine proxy classes and follower classes\:

      if Length( linear ) in [ 2, 5 ] then  # out in [ 3, 6 ]
        prox:= [];
        foll:= [];
        chi:= irreds[ Length( linear ) ];
        for i in [ 1 .. nccl ] do
          if chi[i] = omegasquare then
            Add( foll, i );
          else
            Add( prox, i );
          fi;
        od;
      elif Length( linear ) = 3 then        # out = 4
        prox:= [];
        foll:= [];
        chi:= irreds[2];
        for i in [ 1 .. nccl ] do
          if chi[i] = -I then Add( foll, i ); else Add( prox, i ); fi;
        od;
      else
        prox:= [ 1 .. nccl ];
        foll:= [];
      fi;

      if d = 2 then
        # special case without Galois partners
        for chi in faith do
          for i in Adjust[1] do chi[i]:= - chi[i]; od;
          Add( irreds, chi );
          for lin in linear do
            ext:= List( [ 1 .. nccl ], x -> lin[x] * chi[x] );
            if not ext in irreds then Add( irreds, ext ); fi;
          od;
        od;
      elif d = 12 then
        # special case with three Galois partners and 'lin = []'
        vals:= [ E(12)^7, - omegasquare, - I, E(3), E(12)^11, -1,
                 -E(12)^7, omegasquare, I, -E(3), -E(12)^11 ];
        for j in [ 1 .. Length( faith ) ] do
          chi:= faith[j];
          for i in [ 1 .. 11 ] do
            chi{ Adjust[i] }:= vals[i] * chi{ Adjust[i] };
          od;
          Add( irreds, chi );
          for i in partner[j] do
            Add( irreds, List( chi, x -> GaloisCyc( x, i ) ) );
          od;
        od;
      else

        if d = 3 then
          Adjust{ [ 1, 2 ] }:= [ Union( Intersection( Adjust[1], prox ),
                                        Intersection( Adjust[2], foll ) ),
                                 Union( Intersection( Adjust[2], prox ),
                                        Intersection( Adjust[1], foll ) ) ];
          vals:= [ E(3), E(3)^2 ];
        elif d = 4 then
          Adjust{ [ 1, 3 ] }:= [ Union( Intersection( Adjust[1], prox ),
                                        Intersection( Adjust[3], foll ) ),
                                 Union( Intersection( Adjust[3], prox ),
                                        Intersection( Adjust[1], foll ) ) ];
          vals:= [ I, -1, -I ];
        elif d = 6 then
          vals:= [ -E(3), omegasquare, -1, E(3), - omegasquare ];
        fi;

        for j in [ 1 .. Length( faith ) ] do
          chi:= faith[j];
          for i in [ 1 .. d-1 ] do
            chi{ Adjust[i] }:= vals[i] * chi{ Adjust[i] };
          od;
          Add( irreds, chi );
          for lin in linear do
            ext:= List( [ 1 .. nccl ], x -> lin[x] * chi[x] );
            if not ext in irreds then Add( irreds, ext ); fi;
          od;
          chi:= List( chi, x -> GaloisCyc( x, partner[j] ) );
          Add( irreds, chi );
          for lin in linear do
            ext:= List( [ 1 .. nccl ], x -> lin[x] * chi[x] );
            if not ext in irreds then Add( irreds, ext ); fi;
          od;
        od;

      fi;
    od;
    tbl.irreducibles:= irreds;
end;


#############################################################################
##
#F  ConstructDirectProduct( <tbl>, <factors> )
#F  ConstructDirectProduct( <tbl>, <factors>, <permclasses>, <permchars> )
##
##  special case of a 'construction' call for a library table <tbl>\:
##
##  constructs a direct product of the tables described in the list
##  <factors>, stores all those of its record components in <tbl>
##  that are not yet bound in <tbl>.
##  The 'fusions' component of <tbl> will be enlarged by the fusions of the
##  direct product (factor fusions).
##
##  If the optional arguments <permclasses>, <permchars> are given then
##  classes and characters of the result are sorted accordingly.
##
ConstructDirectProduct := function( arg )
    local tbl, factors, t, i, fld;

    tbl:= arg[1];
    factors:= arg[2];
    t:= CharTableLibrary( factors[1] );
    for i in [ 2 .. Length( factors ) ] do
      t:= CharTableDirectProduct( t, CharTableLibrary( factors[i] ) );
    od;
    if 2 < Length( arg ) then
      SortClassesCharTable( t, arg[3] );
      SortCharactersCharTable( t, arg[4] );
      Unbind( t.permutation );
    fi;
    for fld in Difference( RecFields( t ), RecFields( tbl ) ) do
      tbl.( fld ):= t.( fld );
    od;
    if 1 < Length( factors ) then
      Append( tbl.fusions, t.fusions );
    fi;
end;


#############################################################################
##
#F  ConstructIsoclinic( <tbl>, <factors>[, <nsg>[, <centre>]]
#F                      [, <permclasses>, <permchars>] )
##
ConstructIsoclinic := function( arg )
    local tbl, factors, t, i, args, perms, fld;

    tbl:= arg[1];
    factors:= arg[2];
    t:= CharTableLibrary( factors[1] );
    for i in [ 2 .. Length( factors ) ] do
      t:= CharTableDirectProduct( t, CharTableLibrary( factors[i] ) );
    od;
    args:= Filtered( arg, x -> not IsPerm( x ) );
    if Length( args ) = 2 then
      t:= CharTableIsoclinic( t );
    elif Length( args ) = 3 then
      t:= CharTableIsoclinic( t, args[3] );
    elif Length( args ) = 4 then
      t:= CharTableIsoclinic( t, args[3], args[4] );
    else
      Error( "invalid arguments <arg>" );
    fi;
    perms:= Filtered( arg, IsPerm );
    if Length( perms ) = 2 then
      SortClassesCharTable( t, perms[1] );
      SortCharactersCharTable( t, perms[2] );
    fi;
    for fld in RecFields( t ) do
      if not IsBound( tbl.( fld ) ) then
        tbl.( fld ):= t.( fld );
      fi;
    od;
end;


#############################################################################
##
#F  ConstructV4G( <tbl>, <facttbl>, <aut> )
##
##  Let <tbl> be the character table of a group of type $2^2.G$
##  where an outer automorphism of order 3 permutes the three involutions
##  in the central $2^2$.
##  Let <aut> be the permutation of classes of <tbl> induced by that
##  automorphism, and <facttbl> the name of the character table
##  of the factor group $2.G$.
##  Then 'ConstructV4G' constructs the irreducible characters of <tbl> from
##  that information.
##
ConstructV4G := function( arg )
    local tbl, facttbls, aut, ker, fus, i, chars;

    tbl:= arg[1];
    if Length( arg ) = 2 then
      facttbls:= arg[2];
    else
      facttbls:= [ arg[2] ];
      aut:= arg[3];
    fi;

    fus:= List( facttbls, x -> First( tbl.fusions,
                                      fus -> fus.name = x ).map );
    facttbls:= List( facttbls, CharTable );
    tbl.irreducibles:= List( facttbls[1].irreducibles, x -> x{ fus[1] } );

    if Length( arg ) = 2 then
      for i in [ 2 .. Length( facttbls ) ] do
        Append( tbl.irreducibles, Filtered( List( facttbls[i].irreducibles,
                                          x -> x{ fus[i] } ),
                                      x -> not x in tbl.irreducibles ) );
      od;
    else
      ker:= KernelChar( fus[1] );
      ker:= Difference( OnTuples( ker, aut ), ker )[1];
      chars:= List( Filtered( tbl.irreducibles, x -> x[1] <> x[ ker ] ),
                    x -> Permuted( x, aut ) );
      Append( tbl.irreducibles, chars );
      Append( tbl.irreducibles, List( chars, x -> Permuted( x, aut ) ) );
    fi;
end;


#############################################################################
##
#F  ConstructGS3( <tbls3>, <tbl2>, <tbl3>, <ind2>, <ind3>, <ext>, <perm> )
##
##  constructs the irreducibles of a table <tbls3> of type $G.S_3$ from the
##  tables <tbl2> and <tbl3> of $G.2$ and $G.3$, respectively.
##  <ind2> is a list of numbers denoting irreducibles of <tbl2>.
##  <ind3> is a list of pairs, each denoting irreducibles of <tbl3>.
##  <ext>  is a list of pairs, each denoting one irreducible of <tbl2>
##                             and one of <tbl3>.
##  <perm> is a permutation that must be applied to the irreducibles.
##
ConstructGS3 := function( tbls3, tbl2, tbl3, ind2, ind3, ext, perm )
    local fus2,       # fusion map 'tbl2' in 'tbls3'
          fus3,       # fusion map 'tbl3' in 'tbls3'
          proj2,      # projection $G.S3$ to $G.2$
          pos,        # position in 'proj2'
          proj2i,     # inner part of projection $G.S3$ to $G.2$
          proj2o,     # outer part of projection $G.S3$ to $G.2$
          proj3,      # projection $G.S3$ to $G.3$
          zeroon2,    # zeros for part of $G.2 \setminus G$ in $G.S_3$
          irr,        # irreducible characters of 'tbls3'
          i,          # loop over 'ind2'
          pair,       # loop over 'ind3' and 'ext'
          chi,        # character
          chii,       # inner part of character
          chio;       # outer part of character

    tbl2:= CharTable( tbl2 );
    tbl3:= CharTable( tbl3 );

    fus2:= GetFusionMap( tbl2, tbls3 );
    fus3:= GetFusionMap( tbl3, tbls3 );

    proj2:= ProjectionMap( fus2 );
    pos:= First( [ 1 .. Length( proj2 ) ], x -> not IsBound( proj2[x] ) );
    proj2i:= proj2{ [ 1 .. pos-1 ] };
    pos:= First( [ pos .. Length( proj2 ) ], x -> IsBound( proj2[x] ) );
    proj2o:= proj2{ [ pos .. Length( proj2 ) ] };
    proj3:= ProjectionMap( fus3 );

    zeroon2:= Difference( [ 1 .. Length( tbls3.centralizers ) ], fus3 ) * 0;

    irr:= [];

    # Induce the characters given by 'ind2' from 'tbl2'.
    Append( irr, Induced( tbl2, tbls3, tbl2.irreducibles{ ind2 } ) );

    # Induce the characters given by 'ind3' from 'tbl3'.
    for pair in ind3 do
      chi:= Sum( pair, x -> tbl3.irreducibles[x] );
      Add( irr, Concatenation( chi{ proj3 }, zeroon2 ) );
    od;

    # Put the extensions from 'tbl' together.
    for pair in ext do
      chii:= tbl3.irreducibles[ pair[1] ]{ proj3 };
      chio:= tbl2.irreducibles[ pair[2] ]{ proj2o };
      Add( irr, Concatenation( chii,  chio ) );
      Add( irr, Concatenation( chii, -chio ) );
    od;

    # Permute the characters with 'perm'.
    irr:= Permuted( irr, perm );

    # Store the irreducibles.
    tbls3.irreducibles:= irr;
end;


#############################################################################
##
#F  ConstructPermuted( <tbl>, <libnam>[, <prmclasses>, <prmchars>] )
##
##  The library table <tbl> is completed with help of the library table with
##  name <libnam>, whose classes and characters must be permuted by the
##  permutations <prmclasses> and <prmchars>, respectively.
##
ConstructPermuted := function( arg )
    local tbl, t, fld, automorphisms, irredinfo, classtext, fusions,
          projectives;

    tbl:= arg[1];
    t := CharTableLibrary( arg[2] );
    for fld  in RecFields( t )  do
      if not IsBound( tbl.( fld ) ) then
        tbl.(fld) := t.(fld);
      fi;
    od;
    if IsBound( tbl.automorphisms ) then
      automorphisms:= tbl.automorphisms;
      Unbind( tbl.automorphisms );
    fi;
    if IsBound( tbl.irredinfo ) then
      irredinfo:= tbl.irredinfo;
      Unbind( tbl.irredinfo );
    fi;
    if IsBound( tbl.classtext ) then
      classtext:= tbl.classtext;
      Unbind( tbl.classtext );
    fi;
    if IsBound( tbl.fusions ) then
      fusions:= tbl.fusions;
      tbl.fusions:= [];
    fi;
    if IsBound( tbl.projectives ) then
      projectives:= tbl.projectives;
      Unbind( tbl.projectives );
    fi;
    if 2 < Length( arg ) then
      SortClassesCharTable( tbl, arg[3] );
    fi;
    if 3 < Length( arg ) then
      SortCharactersCharTable( tbl, arg[4] );
    fi;
    Unbind( tbl.permutation );
    if IsBound( automorphisms ) then
      tbl.automorphisms:= automorphisms;
    fi;
    if IsBound( irredinfo ) then
      tbl.irredinfo:= irredinfo;
    fi;
    if IsBound( classtext ) then
      tbl.classtext:= classtext;
    fi;
    if IsBound( fusions ) then
      tbl.fusions:= fusions;
    fi;
    if IsBound( projectives ) then
      tbl.projectives:= projectives;
    fi;
end;


#############################################################################
##
#F  ConstructAdjusted( <tbl>, <libnam>, <pairs>
#F                     [, <permclasses>, <permchars>] )
##
ConstructAdjusted:= function( arg )
    local tbl, t, pair;

    tbl:= arg[1];

    # Get the permuted library table.
    t:= CharTableLibrary( arg[2] );
    if 3 < Length( arg ) and arg[4] <> () then
      SortClassesCharTable( t, arg[4] );
    fi;
    if 4 < Length( arg ) and arg[5] <> () then
      SortCharactersCharTable( t, arg[5] );
    fi;

    # Set the components that shall be adjusted.
    for pair in arg[3] do
      if pair[1] = "ComputedPowerMaps" then
        tbl.powermaps:= pair[2];
      else
        Error( "transfer of component `", pair[1],
               "' is not yet supported by `ConstructAdjusted'" );
      fi;
    od;

    # Transfer not adjusted defining components.
    if not IsBound( tbl.centralizers ) then
      tbl.centralizers:= t.centralizers;
    fi;
    if not IsBound( tbl.powerpaps ) then
      tbl.powermap:= t.powermap;
    fi;
    if not IsBound( tbl.irreducibles ) then
      tbl.irreducibles:= t.irreducibles;
    fi;
    end;


#############################################################################
##
#F  ConstructFactor( <tbl>, <libnam>, <kernel> )
##
##  The library table <tbl> is completed with help of the library table with
##  name <libnam>, whose classes and characters must be permuted by the
##  permutations <prmclasses> and <prmchars>, respectively.
##
ConstructFactor := function( tbl, libnam, kernel )
    local t, fld;
    t:= CharTableFactorGroup( CharTableLibrary( libnam ), kernel );
    for fld  in RecFields( t )  do
      if not IsBound( tbl.( fld ) ) then
        tbl.(fld) := t.(fld);
      fi;
    od;
end;


#############################################################################
##
#F  ConstructSubdirect( <tbl>, <factors>, <choice> )
##
##  The library table <tbl> is completed with help of the table got from
##  taking the direct product of the tables with names in the list <factors>,
##  and then taking the table consisting of the classes in the list <choice>.
##
ConstructSubdirect := function( tbl, factors, choice  )
    local t, i, fld;
    t:= CharTableLibrary( factors[1] );
    for i in [ 2 .. Length( factors ) ] do
      t:= CharTableDirectProduct( t, CharTableLibrary( factors[i] ) );
    od;
    t:= CharTableNormalSubgroup( t, choice );
    for fld in RecFields( t ) do
      if not IsBound( tbl.( fld ) ) then
        tbl.( fld ):= t.( fld );
      fi;
    od;
end;


#############################################################################
##
#F  IrreducibleCharactersOfIndexTwoSubdirectProduct( <irrH1xH2>, <irrG1xG2>,
#F      <H1xH2fusG>, <GfusG1xG2> )
##
##  We do not want to use the table head of the subdirect product because
##  this function is also called by `ConstructIndexTwoSubdirectProduct',
##  and there just a record is available from which the table is computed
##  later.
##
IrreducibleCharactersOfIndexTwoSubdirectProduct:=
    function( irrH1xH2, irrG1xG2, H1xH2fusG, GfusG1xG2 )
    local H1xH2fusG1xG2, restpos, i, rest, pos, irr, zero, proj1, perm,
          proj2, chi, ind, j;

    H1xH2fusG1xG2:= CompositionMaps( GfusG1xG2, H1xH2fusG );

    # Compute which irreducibles of H1xH2 extend to G1xG2.
    restpos:= List( irrH1xH2, x -> [] );
    for i in [ 1 .. Length( irrG1xG2 ) ] do
      rest:= irrG1xG2[i]{ H1xH2fusG1xG2 };
      pos:= Position( irrH1xH2, rest );
      if pos <> false then
        Add( restpos[ pos ], i );
      fi;
    od;
    irr:= [];
    zero:= 0 * GfusG1xG2;
    proj1:= ProjectionMap( H1xH2fusG );
    perm:= Product( List( Filtered( InverseMap( H1xH2fusG ), IsList ),
                          l -> ( l[1], l[2] ) ) );
    if perm = 1 then
      perm:= ();
    fi;
    proj2:= [];
    for i in [ 1 .. Length( proj1 ) ] do
      if IsBound( proj1[i] ) then
        proj2[i]:= proj1[i]^perm;
      fi;
    od;
    for i in [ 1 .. Length( irrH1xH2 ) ] do
      if not IsEmpty( restpos[i] ) then
        # The i-th irreducible of H1xH2 extends to G1xG2.
        # Restrict these extensions to G.
        Append( irr, DuplicateFreeList( List( irrG1xG2{ restpos[i] },
                                              chi -> chi{ GfusG1xG2 } ) ) );
      else
        # The i-th irreducible character of H1xH2 has inertia subgroup one of
        # H1xG2 or G1xH2, so it induces irreducibly to G.
        # Compute the induced character (without using the table head).
        chi:= irrH1xH2[i];

        # The curly bracket operator works only for dense sublists.
        # ind:= ShallowCopy( zero ) + chi{ proj1 } + chi{ proj2 };
        ind:= ShallowCopy( zero );
        for j in [ 1 .. Length( proj1 ) ] do
          if IsBound( proj1[j] ) then
            ind[j]:= ind[j] + chi[ proj1[j] ];
          fi;
        od;
        for j in [ 1 .. Length( proj2 ) ] do
          if IsBound( proj2[j] ) then
            ind[j]:= ind[j] + chi[ proj2[j] ];
          fi;
        od;

        if not ind in irr then
          Add( irr, ind );
        fi;
      fi;
    od;

    return irr;
end;


#############################################################################
##
#F  ClassFusionsForIndexTwoSubdirectProduct( <tblH1>, <tblG1>, <tblH2>,
#F                                           <tblG2> )
##
##  It is assumed that all tables are either ordinary tables or Brauer tables
##  for the same characteristic.
##
##  Note that the components `GfusG1xG2', `Gclasses', `Gorders' refer only to
##  the classes inside the normal subgroup `<tblH1> * <tblH2>'.
##
ClassFusionsForIndexTwoSubdirectProduct:= 0;

ClassFusionsForIndexTwoSubdirectProduct:=
    function( tblH1, tblG1, tblH2, tblG2 )
    local p, H1classes, H2classes, H1orders, H2orders, H1fusG1, H2fusG2,
          inv1, inv2, ncclH2, ncclG2, H1xH2fusG, GfusG1xG2,
          Gclasses, Gorders, i1, i2, posG1xG2, len, pos,
          ordH1, ordG1, ordH2, ordG2, info, modGfusordG, modfus2,
          modG1xG2fusordG1xG2, modH1xH2fusordH1xH2;

    if IsBound( tblH1.prime ) then
      p:= tblH1.prime;
    else
      p:= 0;
    fi;

    if p = 0 then

      H1classes:= tblH1.classes;
      H2classes:= tblH2.classes;
      H1orders:= tblH1.orders;
      H2orders:= tblH2.orders;
      H1fusG1:= GetFusionMap( tblH1, tblG1 );
      if H1fusG1 = false then
        H1fusG1:= RepresentativesFusions( tblH1,
                      SubgroupFusions( tblH1, tblG1 ), tblG1 );
        if Length( H1fusG1 ) <> 1 then
          Error( "fusion <tblH1> to <tblG1> is not determined" );
        fi;
      fi;
      H2fusG2:= GetFusionMap( tblH2, tblG2 );
      if H2fusG2 = false then
        H2fusG2:= RepresentativesFusions( tblH2,
                      SubgroupFusions( tblH2, tblG2 ), tblG2 );
        if Length( H2fusG2 ) <> 1 then
          Error( "fusion <tblH2> to <tblG2> is not determined" );
        fi;
      fi;
      inv1:= InverseMap( H1fusG1 );
      inv2:= InverseMap( H2fusG2 );
      ncclH2:= Length( H2classes );
      ncclG2:= Length( tblG2.classes );
      H1xH2fusG:= [];
      GfusG1xG2:= [];
      Gclasses:= [];
      Gorders:= [];

      for i1 in [ 1 .. Length( inv1 ) ] do
        if IsBound( inv1[ i1 ] ) then
          for i2 in [ 1 .. Length( inv2 ) ] do
            if IsBound( inv2[ i2 ] ) then
              posG1xG2:= ( i1 - 1 ) * ncclG2 + i2;
              if IsInt( inv1[ i1 ] ) then
                if IsInt( inv2[ i2 ] ) then
                  # no fusion
                  len:= Length( GfusG1xG2 ) + 1;
                  H1xH2fusG[ ( inv1[ i1 ] - 1 ) * ncclH2 + inv2[ i2 ] ]:= len;
                  GfusG1xG2[ len ]:= posG1xG2;
                  Gclasses[ len ]:= H1classes[ inv1[ i1 ] ]
                                    * H2classes[ inv2[ i2 ] ];
                  Gorders[ len ]:= LcmInt( H1orders[ inv1[ i1 ] ],
                                           H2orders[ inv2[ i2 ] ] );
                else
                  # fusion from H2 to G2
                  len:= Length( GfusG1xG2 ) + 1;
                  for pos in inv2[ i2 ] do
                    H1xH2fusG[ ( inv1[ i1 ] - 1 ) * ncclH2 + pos ]:= len;
                  od;
                  GfusG1xG2[ len ]:= posG1xG2;
                  Gclasses[ len ]:= 2 * H1classes[ inv1[ i1 ] ]
                                      * H2classes[ inv2[ i2 ][1] ];
                  Gorders[ len ]:= LcmInt( H1orders[ inv1[ i1 ] ],
                                           H2orders[ inv2[ i2 ][1] ] );
                fi;
              elif IsInt( inv2[ i2 ] ) then
                # fusion from H1 to G1
                len:= Length( GfusG1xG2 ) + 1;
                for pos in inv1[ i1 ] do
                  H1xH2fusG[ ( pos - 1 ) * ncclH2 + inv2[ i2 ] ]:= len;
                od;
                GfusG1xG2[ len ]:= posG1xG2;
                Gclasses[ len ]:= 2 * H1classes[ inv1[ i1 ][1] ]
                                    * H2classes[ inv2[ i2 ] ];
                Gorders[ len ]:= LcmInt( H1orders[ inv1[ i1 ][1] ],
                                         H2orders[ inv2[ i2 ] ] );
              else
                # fusion in both factors (get two classes)
                len:= Length( GfusG1xG2 ) + 1;
                H1xH2fusG[ ( inv1[ i1 ][1]-1 ) * ncclH2 + inv2[i2][1] ]:= len;
                H1xH2fusG[ ( inv1[ i1 ][2]-1 ) * ncclH2 + inv2[i2][2] ]:= len;
                GfusG1xG2[ len ]:= posG1xG2;
                Gclasses[ len ]:= 2 * H1classes[ inv1[ i1 ][1] ]
                                    * H2classes[ inv2[ i2 ][1] ];
                Gorders[ len ]:= LcmInt( H1orders[ inv1[ i1 ][1] ],
                                         H2orders[ inv2[ i2 ][1] ] );
                H1xH2fusG[ ( inv1[i1][1]-1 ) * ncclH2 + inv2[i2][2] ]:= len + 1;
                H1xH2fusG[ ( inv1[i1][2]-1 ) * ncclH2 + inv2[i2][1] ]:= len + 1;
                GfusG1xG2[ len + 1 ]:= posG1xG2;
                Gclasses[ len + 1 ]:= Gclasses[ len ];
                Gorders[ len + 1 ]:= Gorders[ len ];
              fi;
            fi;
          od;
        fi;
      od;

    else

      ordH1:= tblH1.ordinary;
      ordG1:= tblG1.ordinary;
      ordH2:= tblH2.ordinary;
      ordG2:= tblG2.ordinary;

      # Compute the maps for the underlying ordinary tables.
      info:= ClassFusionsForIndexTwoSubdirectProduct( ordH1, ordG1,
                                                      ordH2, ordG2 );

      # Compute the embeddings of `p'-regular classes of G, H1xH2, G1xG2,
      # without actually constructing these tables.
      modGfusordG:= Filtered( [ 1 .. Length( info.Gorders ) ],
                              i -> info.Gorders[i] mod p <> 0 );
      modfus2:= GetFusionMap( tblG2, ordG2 );
      modG1xG2fusordG1xG2:= Concatenation(
          List( GetFusionMap( tblG1, ordG1 ),
                i -> modfus2 + ( i - 1 ) * Length( ordG2.classes ) ) );
      modfus2:= GetFusionMap( tblH2, ordH2 );
      modH1xH2fusordH1xH2:= Concatenation(
          List( GetFusionMap( tblH1, ordH1 ),
                i -> modfus2 + ( i - 1 ) * Length( ordH2.classes ) ) );

      # Compute the maps for the Brauer tables.
      H1xH2fusG:= CompositionMaps( InverseMap( modGfusordG ),
                      CompositionMaps( info.H1xH2fusG, modH1xH2fusordH1xH2 ) );
      GfusG1xG2:= CompositionMaps( InverseMap( modG1xG2fusordG1xG2 ),
                      CompositionMaps( info.GfusG1xG2, modGfusordG ) );
      Gclasses:= info.Gclasses{ modGfusordG };
      Gorders:= info.Gorders{ modGfusordG };
    fi;

    return rec( H1xH2fusG:= H1xH2fusG,
                GfusG1xG2:= GfusG1xG2,
                Gclasses:= Gclasses,
                Gorders:= Gorders
              );
end;


#############################################################################
##
#F  ConstructIndexTwoSubdirectProduct( <tbl>, <tblH1>, <tblG1>, <tblH2>,
#F      <tblG2>, <outerfus>, <permclasses>, <permchars> )
##
ConstructIndexTwoSubdirectProduct:= function( tbl, tblH1, tblG1, tblH2, tblG2,
    outerfus, permclasses, permchars )
    local info, irreds;

    tblH1:= CharTable( tblH1 );
    tblG1:= CharTable( tblG1 );
    tblH2:= CharTable( tblH2 );
    tblG2:= CharTable( tblG2 );
    info:= ClassFusionsForIndexTwoSubdirectProduct(
               tblH1, tblG1, tblH2, tblG2 );
    irreds:= IrreducibleCharactersOfIndexTwoSubdirectProduct(
                 KroneckerProduct( tblH1.irreducibles, tblH2.irreducibles ),
                 KroneckerProduct( tblG1.irreducibles, tblG2.irreducibles ),
                 info.H1xH2fusG, Concatenation( info.GfusG1xG2, outerfus ) );
    tbl.irreducibles:= Permuted(
                         List( irreds, chi -> Permuted( chi, permclasses ) ),
                         permchars );
end;


#############################################################################
##
#F  ConstructWreathSymmetric( <tbl>, <subname>, <n>
#F                            [, <permclasses>, <permchars>] )
##
ConstructWreathSymmetric:= function( arg )
    local tbl, sub, t, fld;

    tbl:= arg[1];
    sub:= CharTableLibrary( arg[2] );
    t:= CharTableWreathSymmetric( sub, arg[3] );
    if 3 < Length( arg ) then
      SortClassesCharTable( t, arg[4] );
      SortCharactersCharTable( t, arg[5] );
      if not IsBound( tbl.permutation ) then
        # Do *not* inherit the permutation from the construction!
        tbl.permutation:= ();
      fi;
    fi;
    for fld in Difference( RecFields( t ), RecFields( tbl ) ) do
      tbl.( fld ):= t.( fld );
    od;
end;


#############################################################################
##
#F  UnpackedCll( <cll> )
##
##  is a record with the components 'mat', 'inertiagrps', 'fusionclasses',
##  and perhaps 'libname'.
##  These are the only components used in the construction of library
##  character tables encoded by Clifford matrices.
##
##  The meaning of <cll> is the same as in 'CllToClf'.
##
UnpackedCll := function( cll )
    local l, clmlist,  # library list of the possible matrices
          clf,         # Clifford matrix record, result
          pi;          # permutation to sort library matrices

    # Initialize the Clifford matrix record.
    clf:= rec(
               inertiagrps   := cll[1], 
               fusionclasses := cll[2]
              );

    if Length( cll[2] ) = 1 then

      clf.mat:= [ [ 1 ] ];

    elif Length( cll[3] ) = 2 then

      # is already unpacked, for example dimension 2
      clf.mat:= cll[3];

    else

      # Fetch the matrix from the library.
      cll:= cll[3];
      clf.libname:= cll;
      l:= cll[2];
      clmlist:= LibraryTables( Concatenation( "clm", cll[1] ) );
      if clmlist = false or not IsBound( clmlist[l] ) then
        Error( "sorry, component <mat> not found in the library" );
      fi;

      clf.mat:= Copy( clmlist[l][ cll[3] ] );

      # Sort the rows and columns of the Clifford matrix
      # w.r.t. the explicitly given permutations.
      if IsBound( cll[4] ) then
        clf.mat:= Permuted( clf.mat, cll[4] );
      fi;
      if IsBound( cll[5] ) then
        pi:= cll[5];
        clf.mat:= List( clf.mat, x -> Permuted( x, pi ) );
      fi;

    fi; 

    return clf;
end;


#############################################################################
##
#F  CllToClf( <tbl>, <cll> )
##
##  is a Clifford matrix for the table <tbl>.
##  It is constructed from the list <cll> that contains
##  the following entries.
##  1. list of indices of inertia factors
##  2. list of classes fusing in the factor group
##  3. identification of the matrix,
##     either unbound (then the matrix has dimension <= 2)
##     or a list containing
##       a. string '"elab"' or '"exsp"'
##       b. size of the Clifford matrix
##       c. index in the library file
##       d. (optional) necessary permutation of columns
##     or a list containing
##       a. the Clifford matrix itself and
##       b. the column weights.
##  4. (case '"exsp"') a list with items of record 'splitinfos':
##       a. classindex
##       b. p
##       c. numclasses
##       d. root
##
CllToClf := function( tbl, cll )
    local Ti,          # 
          factor,      # character table of the factor group G/N
          i, nr,
          dim,         # dimension of the matrix
          clf,         # expanded record
          pos,
          map;

    Ti:= tbl.cliffordTable.Ti;
    factor:= Ti.tables[1]; 
--> --------------------

--> maximum size reached

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

[ zur Elbe Produktseite wechseln0.156Quellennavigators  ]