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

SSL ctblothe.gi   Interaktion und
Portierbarkeitunbekannt

 
#############################################################################
##
#W  ctblothe.gi          GAP 4 package CTblLib                  Thomas Breuer
##
##  This file contains the declarations of functions for interfaces to
##  other data formats of character tables.
##
##  1. interface to CAS
##  2. interface to MOC
##  3. interface to GAP 3
##  4. interface to the Cambridge format
##  5. Interface to the MAGMA display format
##


#############################################################################
##
##  1. interface to CAS
##


#############################################################################
##
#F  CASString( <tbl> )
##
InstallGlobalFunction( CASString, function( tbl )
    local ll,                 # line length
          CAS,                # the string, result
          i, j,               # loop variables
          convertcyclotom,    # local function, string of cyclotomic
          convertrow,         # local function, convert a whole list
          column,
          param,              # list of class parameters
          fus,                # loop over fusions
          tbl_irredinfo;

    ll:= SizeScreen()[1];

    if HasIdentifier( tbl ) then                        # name
      CAS:= Concatenation( "'", Identifier( tbl ), "'\n" );
    else
      CAS:= "'NN'\n";
    fi;
    Append( CAS, "00/00/00. 00.00.00.\n" );             # date
    if HasSizesCentralizers( tbl ) then                 # nccl, cvw, ctw
      Append( CAS, "(" );
      Append( CAS, String( Length( SizesCentralizers( tbl ) ) ) );
      Append( CAS, "," );
      Append( CAS, String( Length( SizesCentralizers( tbl ) ) ) );
      Append( CAS, ",0," );
    else
      Append( CAS, "(0,0,0," );
    fi;

    if HasIrr( tbl ) then
      Append( CAS, String( Length( Irr( tbl ) ) ) );    # max
      Append( CAS, "," );
      if Length( Irr( tbl ) ) = Length( Set( Irr( tbl ) ) ) then
        Append( CAS, "-1," );                           # link
      else
        Append( CAS, "0," );                            # link
      fi;
    fi;
    Append( CAS, "0)\n" );                              # tilt
    if HasInfoText( tbl ) then                          # text
      Append( CAS, "text:\n(#" );
      Append( CAS, InfoText( tbl ) );
      Append( CAS, "#),\n" );
    fi;

    convertcyclotom:= function( cyc )
    local i, str, coeffs;
    coeffs:= COEFFS_CYC( cyc );
    str:= Concatenation( "\n<w", String( Length( coeffs ) ), "," );
    if coeffs[1] <> 0 then
      Append( str, String( coeffs[1] ) );
    fi;
    i:= 2;
    while i <= Length( coeffs ) do
      if Length( str ) + Length( String( coeffs[i] ) )
                       + Length( String( i-1 ) ) + 4 >= ll then
        Append( CAS, str );
        Append( CAS, "\n" );
        str:= "";
      fi;
      if coeffs[i] < 0 then
        Append( str, "-" );
        if coeffs[i] <> -1 then
          Append( str, String( -coeffs[i] ) );
        fi;
        Append( str, "w" );
        Append( str, String( i-1 ) );
      elif coeffs[i] > 0 then
        Append( str, "+" );
        if coeffs[i] <> 1 then
          Append( str, String( coeffs[i] ) );
        fi;
        Append( str, "w" );
        Append( str, String( i-1 ) );
      fi;
      i:= i+1;
    od;
    Append( CAS, str );
    Append( CAS, "\n>\n" );
    end;

    convertrow:= function( list )
    local i, str;
    if IsCycInt( list[1] ) and not IsInt( list[1] ) then
      convertcyclotom( list[1] );
      str:= "";
    elif IsUnknown( list[1] ) or IsList( list[1] ) then
      str:= "?";
    else
      str:= ShallowCopy( String( list[1] ) );
    fi;
    i:= 2;
    while i <= Length( list ) do
      if IsCycInt( list[i] ) and not IsInt( list[i] ) then
        Append( CAS, str );
        Append( CAS, "," );
        convertcyclotom( list[i] );
        str:= "";
      elif IsUnknown( list[i] ) or IsList( list[i] ) then
        if Length( str ) + 4 < ll then
          Append( str, ",?" );
        else
          Append( CAS, str );
          Append( CAS, ",?\n" );
          str:= "";
        fi;
      else
        if Length(str) + Length( String(list[i]) ) + 5 < ll then
          Append( str, "," );
          Append( str, String( list[i] ) );
        else
          Append( CAS, str );
          Append( CAS, ",\n" );
          str:= ShallowCopy( String( list[i] ) );
        fi;
      fi;
      i:= i+1;
    od;
    Append( CAS, str );
    Append( CAS, "\n" );
    end;

    Append( CAS, "order=" );                            # order
    Append( CAS, String( Size( tbl ) ) );
    if HasSizesCentralizers( tbl ) then                 # centralizers
      Append( CAS, ",\ncentralizers:(\n" );
      convertrow( SizesCentralizers( tbl ) );
      Append( CAS, ")" );
    fi;
    if HasOrdersClassRepresentatives( tbl ) then        # orders
      Append( CAS, ",\nreps:(\n" );
      convertrow( OrdersClassRepresentatives( tbl ) );
      Append( CAS, ")" );
    fi;
    if HasComputedPowerMaps( tbl ) then                 # power maps
      for i in [ 1 .. Length( ComputedPowerMaps( tbl ) ) ] do
        if IsBound( ComputedPowerMaps( tbl )[i] ) then
          Append( CAS, ",\npowermap:" );
          Append( CAS, String(i) );
          Append( CAS, "(\n" );
          convertrow( ComputedPowerMaps( tbl )[i] );
          Append( CAS, ")" );
        fi;
      od;
    fi;
    if HasClassParameters( tbl )                        # classtext
       and ForAll( ClassParameters( tbl ),              # (partitions only)
                   x ->     IsList( x ) and Length( x ) = 2
                        and x[1] = 1 and IsList( x[2] )
                        and ForAll( x[2], IsPosInt ) ) then
      Append( CAS, ",\nclasstext:'part'\n($[" );
      param:= ClassParameters( tbl );
      convertrow( param[1][2] );
      Append( CAS, "]$" );
      for i in [ 2 .. Length( param ) ] do
        Append( CAS, "\n,$[" );
        convertrow( param[i][2] );
        Append( CAS, "]$" );
      od;
      Append( CAS, ")" );
    fi;
    if HasComputedClassFusions( tbl ) then              # fusions
      for fus in ComputedClassFusions( tbl ) do
        if IsBound( fus.type ) then
          if fus.type = "normal" then
            Append( CAS, ",\nnormal subgroup " );
          elif fus.type = "factor" then
            Append( CAS, ",\nfactor " );
          else
            Append( CAS, ",\n" );
          fi;
        else
          Append( CAS, ",\n" );
        fi;
        Append( CAS, "fusion:'" );
        Append( CAS, fus.name );
        Append( CAS, "'(\n" );
        convertrow( fus.map );
        Append( CAS, ")" );
      od;
    fi;
    if HasIrr( tbl ) then                              # irreducibles
      Append( CAS, ",\ncharacters:" );
      for i in Irr( tbl ) do
        Append( CAS, "\n(" );
        convertrow( i );
        Append( CAS, ",0:0)" );
      od;
    fi;
    if HasComputedPrimeBlockss( tbl ) then             # blocks
      for i in [ 2 .. Length( ComputedPrimeBlockss( tbl ) ) ] do
        if IsBound( ComputedPrimeBlockss( tbl )[i] ) then
          Append( CAS, ",\nblocks:" );
          Append( CAS, String( i ) );
          Append( CAS, "(\n" );
          convertrow( ComputedPrimeBlockss( tbl )[i] );
          Append( CAS, ")" );
        fi;
      od;
    fi;
    if HasComputedIndicators( tbl ) then               # indicators
      for i in [ 2 .. Length( ComputedIndicators( tbl ) ) ] do
        if IsBound( ComputedIndicators( tbl )[i] ) then
          Append( CAS, ",\nindicator:" );
          Append( CAS, String( i ) );
          Append( CAS, "(\n" );
          convertrow( ComputedIndicators( tbl )[i] );
          Append( CAS, ")" );
        fi;
      od;
    fi;
    if 27 < ll then
      Append( CAS, ";\n/// converted from GAP" );
    else
      Append( CAS, ";\n///" );
    fi;
    return CAS;
end );


#############################################################################
##
##  2. interface to MOC
##


#############################################################################
##
#F  MOCFieldInfo( <F> )
##
##  For a number field <F>,
##  'MOCFieldInfo' returns a record with the following components.
##
##  'nofcyc':
##      the conductor of <F>,
##
##  'repres':
##      a list of orbit representatives forming the Parker base of <F>,
##
##  'stabil':
##      a smallest generating system of the stabilizer, and
##
##  'ParkerBasis':
##      the Parker basis of <F>.
##
BindGlobal( "MOCFieldInfo", function( F )
    local i, j, n, orbits, stab, cycs, coeffs, base, repres, rank, max, pos,
          sub, sub2, stabil, elm, numbers, orb, orders, gens;

    if F = Rationals then
      return rec(
                  nofcyc      := 1,
                  repres      := [ 0 ],
                  stabil      := [],
                  ParkerBasis := Basis( Rationals )
                 );
    fi;

    n:= Conductor( F );

    # representatives of orbits under the action of 'GaloisStabilizer( F )'
    # on '[ 0 .. n-1 ]'
    numbers:= [ 0 .. n-1 ];
    orbits:= [];
    stab:= GaloisStabilizer( F );
    while not IsEmpty( numbers ) do
      orb:= Set( List( numbers[1] * stab, x -> x mod n ) );
      Add( orbits, orb );
      SubtractSet( numbers, orb );
    od;

    # orbit sums under the corresponding action on 'n'--th roots of unity
    cycs:= List( orbits, x -> Sum( x, y -> E(n)^y, 0 ) );
    coeffs:= List( cycs, x -> CoeffsCyc( x, n ) );

    # Compute the Parker basis.
    gens:= [ 1 ];
    base:= [ coeffs[1] ];
    repres:= [ 0 ];
    rank:= 1;

    for i in [ 1 .. Length( coeffs ) ] do
      if rank < RankMat( Union( base, [ coeffs[i] ] ) ) then
        rank:= rank + 1;
        Add( gens, cycs[i] );
        Add( base, coeffs[i] );
        Add( repres, orbits[i][1] );
      fi;
    od;

    # Compute a small generating system for the stabilizer:
    # Start with the empty generating system.
    # Add the smallest number of maximal multiplicative order to
    # the generating system, remove all points in the new group.
    # Proceed until one has a generating system for the stabilizer.
    orders:= List( stab, x -> OrderMod( x, n ) );
    orders[1]:= 0;
    max:= Maximum( orders );
    stabil:= [];
    sub:= [ 1 ];
    while max <> 0 do
      pos:= Position( orders, max );
      elm:= stab[ pos ];
      AddSet( stabil, elm );
      sub2:= sub;
      for i in [ 1 .. max-1 ] do
        sub2:= Union( sub2, List( sub, x -> ( x * elm^i ) mod n ) );
      od;
      sub:= sub2;
      for j in sub do
        orders[ Position( stab, j ) ]:= 0;
      od;
      max:= Maximum( orders );
    od;

    return rec(
                nofcyc      := n,
                repres      := repres,
                stabil      := stabil,
                ParkerBasis := Basis( F, gens )
               );
    end );


#############################################################################
##
#F  MAKElb11( <listofns> )
##
InstallGlobalFunction( MAKElb11, function( listofns )
    local n, f, k, j, fields, info, num, stabs;

    # 12 entries per row
    num:= 12;

    for n in listofns do

      if n > 2 and n mod 4 <> 2 then

        fields:= Filtered( Subfields( CF(n) ), x -> Conductor( x ) = n );
        fields:= List( fields, MOCFieldInfo );
        stabs:=  List( fields,
                       x -> Concatenation( [ x.nofcyc, Length( x.repres ),
                                           Length(x.stabil) ], x.stabil ) );
        fields:= List( fields,
                       x -> Concatenation( [ x.nofcyc, Length( x.repres ) ],
                                           x.repres, [ Length( x.stabil ) ],
                                           x.stabil ) );

        # sort fields according to degree and stabilizer generators
        fields:= Permuted( fields, Sortex( stabs ) );
        for f in fields do
          for k in [ 0 .. QuoInt( Length( f ), num ) - 1 ] do
            for j in [ 1 .. num ] do
              Print( String( f[ k*num + j ], 4 ) );
            od;
            Print( "\n " );
          od;
          for j in [ num * QuoInt( Length(f), num ) + 1 .. Length(f) ] do
            Print( String( f[j], 4 ) );
          od;
          Print( "\n" );
        od;

      fi;

    od;
end );


#############################################################################
##
#F  MOCPowerInfo( <listofbases>, <galoisfams>, <powermap>, <prime> )
##
##  For a list <listofbases> of number field bases as produced in
##  'MOCTable' (see~"MOCTable"),
##  the information of labels '30220' and '30230' is computed.
##  This is a sequence
##  $$
##  x_{1,1} x_{1,2} \ldots x_{1,m_1} 0 x_{2,1} x_{2,2} \ldots x_{2,m_2}
##  0 \ldots 0 x_{n,1} x_{n,2} \ldots x_{n,m_n} 0
##  $$
##  with the followong meaning.
##  Let $[ a_1, a_2, \ldots, a_n ]$ be a character in MOC format.
##  The value of the character obtained on indirection by the <prime>-th
##  power map at position $i$ is
##  $$
##  x_{i,1} a_{x_{i,2}} + x_{i,3} a_{x_{i,4}} + \ldots
##  + x_{i,m_i-1} a_{x_{i,m_i}} \ .
##  $$
##
##  The information is computed as follows.
##
##  If $g$ and $g^{<prime>}$ generate the same cyclic group then write the
##  <prime>-th conjugates of the base vectors $v_1, \ldots, v_k$ as
##  $\tilde{v_i} = \sum_{j=1}^{k} c_{ij} v_j$.
##  The $j$-th coefficient of the <prime>-th conjugate of
##  $\sum_{i=1}^{k} a_i v_i$ is then $\sum_{i=1}^{k} a_i c_{ij}$.
##
##  If $g$ and $g^{<prime>}$ generate different cyclic groups then write the
##  base vectors $w_1, \ldots, w_{k^{\prime}}$ in terms of the $v_i$ as
##  $w_i = \sum_{j=1}^{k} c_{ij} v_j$.
##  The $v_j$-coefficient of the indirection of
##  $\sum_{i=1}^{k^{\prime}} a_i w_i$ is then
##  $\sum_{i=1}^{k^{\prime}} a_i c_{ij}$.
##
##  For $<prime> = -1$ (complex conjugation) we have of course
##  $k = k^{\prime}$ and $w_i = \overline{v_i}$.
##  In this case the parameter <powermap> may have any value.
##  Otherwise <powermap> must be the 'ComputedPowerMaps' value of the
##  underlying character table;
##  for any Galois automorphism of a cyclic subgroup,
##  it must contain a map covering this automorphism.
##
##  <galoisfams> is a list that describes the Galois conjugacy;
##  its format is equal to that of the 'galoisfams' component in
##  records returned by 'GaloisMat'.
##
##  'MOCPowerInfo' returns a list containing the information for <prime>,
##  the part of class 'i' is stored in a list at position 'i'.
##
##  *Note* that 'listofbases' refers to all classes, not only
##  representatives of cyclic subgroups;
##  non-leader classes of Galois families must have value 0.
##
BindGlobal( "MOCPowerInfo",
    function( listofbases, galoisfams, powermap, prime )
    local power, i, f, c, im, oldim, imf, pp, entry, j, n, k;

    power:= [];
    i:= 1;
    while i <= Length( listofbases ) do

      if (     IsBasis( listofbases[i] )
           and UnderlyingLeftModule( listofbases[i] ) = Rationals )
         or listofbases[i] = 1 then

        # rational class
        if prime = -1 then
          Add( power, [ 1, i, 0 ] );
        else

          # 'prime'-th power of class 'i' (of course rational)
          Add( power, [ 1, powermap[ prime ][i], 0 ] );

        fi;

      elif listofbases[i] <> 0 then

        # the field basis
        f:= listofbases[i];

        if prime = -1 then

          # the coefficient matrix
          c:= List( BasisVectors( f ),
                    x -> Coefficients( f, GaloisCyc( x, -1 ) ) );
          im:= i;

        else

          # the image class and field
          oldim:= powermap[ prime ][i];
          if galoisfams[ oldim ] = 1 then
            im:= oldim;
          else
            im:= 1;
            while not IsList( galoisfams[ im ] ) or
                  not oldim in galoisfams[ im ][1] do
              im:= im+1;
            od;
          fi;

          if listofbases[ im ] = 1 then

            # maps to rational class 'im'
            c:= [ Coefficients( f, 1 ) ];

          elif im = i then

            # just Galois conjugacy
            c:= List( BasisVectors( f ),
                      x -> Coefficients( f, GaloisCyc(x,prime) ) );

          else

            # compute embedding of the image field
            imf:= listofbases[ im ];
            pp:= false;
            for j in [ 2 .. Length( powermap ) ] do
              if IsBound( powermap[j] ) and powermap[j][ im ] = oldim then
                pp:= j;
              fi;
            od;
            if pp = false then
              Error( "MOCPowerInfo cannot compute Galois autom. for ", im,
                     " -> ", oldim, " from power map" );
            fi;

            c:= List( BasisVectors( imf ),
                      x -> Coefficients( f, GaloisCyc(x,pp) ) );

          fi;

        fi;

        # the power info for column 'i' of the MOC table,
        # and all other columns in the same cyclic subgroup
        entry:= [];
        n:= Length( c );
        for j in [ 1 .. Length( c[1] ) ] do
          for k in [ 1 .. n ] do
            if c[k][j] <> 0 then
              Append( entry, [ c[k][j], im + k - 1 ] );
#T this assumes that Galois families are subsequent!
            fi;
          od;
          Add( entry, 0 );
        od;
        Add( power, entry );

      fi;
      i:= i+1;
    od;
    return power;
end );


#############################################################################
##
#F  ScanMOC( <list> )
##
InstallGlobalFunction( ScanMOC, function( list )
    local digits, positive, negative, specials,
          admissible,
          number,
          pos, result,
          scannumber2,     # scan a number in MOC 2 format
          scannumber3,     # scan a number in MOC 3 format
          label, component;

    # Check the argument.
    if not IsList( list ) then
      Error( "argument must be a list" );
    fi;

    # Define some constants used for MOC 3 format.
    digits:= "0123456789";
    positive:= "abcdefghij";
    negative:= "klmnopqrs";
    specials:= "tuvwyz";

    # Remove characters that are nonadmissible, for example line breaks.
    admissible:= Union( digits, positive, negative, specials );
    list:= Filtered( list, char -> char in admissible );

    # local functions: scan a number of MOC 2 or MOC 3 format
    scannumber2:= function()
    number:= 0;
    while list[ pos ] < 10000 do

      # number is not complete
      number:= 10000 * number + list[ pos ];
      pos:= pos + 1;
    od;
    if list[ pos ] < 20000 then
      number:= 10000 * number + list[ pos ] - 10000;
    else
      number:= - ( 10000 * number + list[ pos ] - 20000 );
    fi;
    pos:= pos + 1;
    return number;
    end;

    scannumber3:= function()
    number:= 0;
    while list[ pos ] in digits do

      # number is not complete
      number:=  10000 * number
               + 1000 * Position( digits, list[ pos   ] )
               +  100 * Position( digits, list[ pos+1 ] )
               +   10 * Position( digits, list[ pos+2 ] )
               +        Position( digits, list[ pos+3 ] )
               - 1111;
      pos:= pos + 4;
    od;

    # end of number or small number
    if list[ pos ] in positive then

      # small positive number
      if number <> 0 then
        Error( "corrupted input" );
      fi;
      number:=   10000 * number
               + Position( positive, list[ pos ] )
               - 1;

    elif list[ pos ] in negative then

      # small negative number
      if number <> 0 then
        Error( "corrupted input" );
      fi;
      number:=   10000 * number
               - Position( negative, list[ pos ] );

    elif   list[ pos ] = 't' then
      number:=   10000 * number
               + 10 * Position( digits, list[ pos+1 ] )
               +      Position( digits, list[ pos+2 ] )
               - 11;
      pos:= pos + 2;
    elif list[ pos ] = 'u' then
      number:=   10000 * number
               - 10 * Position( digits, list[ pos+1 ] )
               -      Position( digits, list[ pos+2 ] )
               + 11;
      pos:= pos + 2;
    elif list[ pos ] = 'v' then
      number:=   10000 * number
               + 1000 * Position( digits, list[ pos+1 ] )
               +  100 * Position( digits, list[ pos+2 ] )
               +   10 * Position( digits, list[ pos+3 ] )
               +        Position( digits, list[ pos+4 ] )
               - 1111;
      pos:= pos + 4;
    elif list[ pos ] = 'w' then
      number:= - 10000 * number
               - 1000 * Position( digits, list[ pos+1 ] )
               -  100 * Position( digits, list[ pos+2 ] )
               -   10 * Position( digits, list[ pos+3 ] )
               -        Position( digits, list[ pos+4 ] )
               + 1111;
      pos:= pos + 4;
    fi;
    pos:= pos + 1;
    return number;
    end;

    # convert <list>
    result:= rec();
    pos:= 1;

    if IsInt( list[1] ) then

      # MOC 2 format
      if list[1] = 30100 then pos:= 2; fi;
      while pos <= Length( list ) and list[ pos ] <> 31000 do
        label:= list[ pos ];
        pos:= pos + 1;
        component:= [];
        while pos <= Length( list ) and list[ pos ] < 30000 do
          Add( component, scannumber2() );
        od;
        result.( label ):= component;
      od;

    else

      # MOC 3 format
      if list{ [ 1 .. 4 ] } = "y100" then
        pos:= 5;
      fi;

      while pos <= Length( list ) and list[ pos ] <> 'z' do

        # label of form 'yABC'
        label:= list{ [ pos .. pos+3 ] };
        pos:= pos + 4;
        component:= [];
        while pos <= Length( list ) and not list[ pos ] in "yz" do
          Add( component, scannumber3() );
        od;
        result.( label ):= component;
      od;
    fi;

    return result;
end );


#############################################################################
##
#F  MOCChars( <tbl>, <gapchars> )
##
InstallGlobalFunction( MOCChars, function( tbl, gapchars )
    local i, result, chi, MOCchi;

    # take the MOC format (if necessary, construct the MOC format table first)
    if IsCharacterTable( tbl ) then
      tbl:= MOCTable( tbl );
    fi;

    # translate the characters
    result:= [];
    for chi in gapchars do
      MOCchi:= [];
      for i in [ 1 .. Length( tbl.fieldbases ) ] do
        if UnderlyingLeftModule( tbl.fieldbases[i] ) = Rationals then
          Add( MOCchi, chi[ tbl.repcycsub[i] ] );
        else
          Append( MOCchi, Coefficients( tbl.fieldbases[i],
                                        chi[ tbl.repcycsub[i] ] ) );
        fi;
      od;
      Add( result, MOCchi );
    od;
    return result;
end );


#############################################################################
##
#F  GAPChars( <tbl>, <mocchars> )
##
InstallGlobalFunction( GAPChars, function( tbl, mocchars )
    local i, j, val, result, chi, GAPchi, map, pos, numb, nccl;

    # take the MOC format table (if necessary, construct it first)
    if IsCharacterTable( tbl ) then
      tbl:= MOCTable( tbl );
    fi;

    # 'map[i]' is the list of columns of the MOC table that belong to
    # the 'i'-th cyclic subgroup of the MOC table
    map:= [];
    pos:= 0;
    for i in [ 1 .. Length( tbl.fieldbases ) ] do
      Add( map, pos + [ 1 .. Length( BasisVectors( tbl.fieldbases[i] ) ) ] );
      pos:= pos + Length( BasisVectors( tbl.fieldbases[i] ) );
    od;

    result:= [];

    # if 'mocchars' is not a list of lists, divide it into pieces of length
    # 'nccl'
    if not IsList( mocchars[1] ) then
      nccl:= NrConjugacyClasses( tbl.GAPtbl );
      mocchars:= List( [ 1 .. Length( mocchars ) / nccl ],
                       i -> mocchars{ [ (i-1)*nccl+1 .. i*nccl ] } );
    fi;

    for chi in mocchars do
      GAPchi:= [];
      # loop over classes of the GAP table
      for i in [ 1 .. Length( tbl.galconjinfo ) / 2 ] do

        # the number of the cyclic subgroup in the MOC table
        numb:= tbl.galconjinfo[ 2*i - 1 ];
        if UnderlyingLeftModule( tbl.fieldbases[ numb ] ) = Rationals then

          # rational class
          GAPchi[i]:= chi[ map[ tbl.galconjinfo[ 2*i-1 ] ][1] ];

        elif tbl.galconjinfo[ 2*i ] = 1 then

          # representative of cyclic subgroup, not rational
          GAPchi[i]:= chi{ map[ numb ] }
                      * BasisVectors( tbl.fieldbases[ numb ] );

        else

          # irrational class, no representative:
          # conjugate the value on the representative class
          GAPchi[i]:=
             GaloisCyc( GAPchi[ ( Position( tbl.galconjinfo, numb ) + 1 ) / 2 ],
                        tbl.galconjinfo[ 2*i ] );

        fi;
      od;
      Add( result, GAPchi );
    od;
    return result;
end );


#############################################################################
##
#F  MOCTable0( <gaptbl> )
##
##  MOC 3 format table of ordinary GAP table <gaptbl>
##
BindGlobal( "MOCTable0", function( gaptbl )
    local i, j, k, d, n, p, result, trans, gal, extendedfields, entry,
          gaptbl_orders, vectors, prod, pow, im, cl, basis, struct, rep,
          aut, primes;

    # initialize the record
    result:= rec( identifier := Concatenation( "MOCTable(",
                                               Identifier( gaptbl ), ")" ),
                  prime  := 0,
                  fields := [],
                  GAPtbl := gaptbl );

    # 1. Compute necessary information to encode the irrational columns.
    #
    #    Each family of $n$ Galois conjugate classes is replaced by $n$
    #    integral columns, the Parker basis of each number field
    #    is stored in the component 'fieldbases' of the result.
    #
    trans:= TransposedMat( Irr( gaptbl ) );
    gal:= GaloisMat( trans ).galoisfams;

    result.cycsubgps:= [];
    result.repcycsub:= [];
    result.galconjinfo:= [];
    for i in [ 1 .. Length( gal ) ] do
      if gal[i] = 1 then
        Add( result.repcycsub, i );
        result.cycsubgps[i]:= Length( result.repcycsub );
        Append( result.galconjinfo, [ Length( result.repcycsub ), 1 ] );
      elif gal[i] <> 0 then
        Add( result.repcycsub, i );
        n:= Length( result.repcycsub );
        for k in gal[i][1] do
          result.cycsubgps[k]:= n;
        od;
        Append( result.galconjinfo, [ Length( result.repcycsub ), 1 ] );
      else
        rep:= result.repcycsub[ result.cycsubgps[i] ];
        aut:= gal[ rep ][2][ Position( gal[ rep ][1], i ) ]
                 mod Conductor( trans[i] );
        Append( result.galconjinfo, [ result.cycsubgps[i], aut ] );
      fi;
    od;

    gaptbl_orders:= OrdersClassRepresentatives( gaptbl );

    # centralizer orders and element orders
    # (for representatives of cyclic subgroups only)
    result.centralizers:= SizesCentralizers( gaptbl ){ result.repcycsub };
    result.orders:= OrdersClassRepresentatives( gaptbl ){ result.repcycsub };

    # the fields (for cyclic subgroups only)
    result.fieldbases:= List( result.repcycsub,
                        i -> MOCFieldInfo( Field( trans[i] ) ).ParkerBasis );

    # fields for all classes (used by 'MOCPowerInfo')
    extendedfields:= List( [ 1 .. Length( gal ) ], x -> 0 );
    for i in [ 1 .. Length( result.repcycsub ) ] do
      extendedfields[ result.repcycsub[i] ]:= result.fieldbases[i];
    od;

    # '30170' power maps:
    # for each cyclic subgroup (except the trivial one) and each prime
    # divisor of the representative order store four values, the number
    # of the subgroup, the power, the number of the cyclic subgroup
    # containing the image, and the power to which the representative
    # must be raised to give the image class.
    # (This is used only to construct the '30230' power map/embedding
    # information.)
    # In 'result.30170' only a list of lists (one for each cyclic subgroup)
    # of all these values is stored, it will not be used by GAP.
    #
    result.30170:= [ [] ];
    for i in [ 2 .. Length( result.repcycsub ) ] do

      entry:= [];
      for d in PrimeDivisors( gaptbl_orders[ result.repcycsub[i] ] ) do

        # cyclic subgroup 'i' to power 'd'
        Add( entry, i );
        Add( entry, d );
        pow:= PowerMap( gaptbl, d )[ result.repcycsub[i] ];

        if gal[ pow ] = 1 then

          # rational class
          Add( entry, Position( result.repcycsub, pow ) );
          Add( entry, 1 );

        else

          # get the representative 'im'
          im:= result.repcycsub[ result.cycsubgps[ pow ] ];
          cl:= Position( gal[ im ][1], pow );

          # the image is class 'im' to power 'gal[ im ][2][cl]'
          Add( entry, Position( result.repcycsub, im ) );
          Add( entry, gal[ im ][2][cl]
                              mod gaptbl_orders[ result.repcycsub[i] ] );

        fi;

      od;

      Add( result.30170, entry );

    od;

    # tensor product information, used to compute the coefficients of
    # the Parker base for tensor products of characters.
    result.tensinfo:= [];
    for basis in result.fieldbases do
      if UnderlyingLeftModule( basis ) = Rationals then
        Add( result.tensinfo, [ 1 ] );
      else
        vectors:= BasisVectors( basis );
        n:= Length( vectors );

        # Compute structure constants.
        struct:= List( vectors, x -> [] );
        for i in [ 1 .. n ] do
          for k in [ 1 .. n ] do
            struct[k][i]:= [];
          od;
          for j in [ 1 .. n ] do
            prod:= Coefficients( basis, vectors[i] * vectors[j] );
            for k in [ 1 .. n ] do
              struct[k][i][j]:= prod[k];
            od;
          od;
        od;

        entry:= [ n ];
        for i in [ 1 .. n ] do
          for j in [ 1 .. n ] do
            for k in [ 1 .. n ] do
              if struct[i][j][k] <> 0 then
                Append( entry, [ struct[i][j][k], j, k ] );
              fi;
            od;
          od;
          Add( entry, 0 );
        od;
        Add( result.tensinfo, entry );
      fi;
    od;

    # '30220' inverse map (to compute complex conjugate characters)
    result.invmap:= MOCPowerInfo( extendedfields, gal, 0, -1 );

    # '30230' power map (field embeddings for $p$-th symmetrizations,
    # where $p$ is a prime not larger than the maximal element order);
    # note that the necessary power maps must be stored on 'gaptbl'
    result.powerinfo:= [];
    primes:= Filtered( [ 2 .. Maximum( gaptbl_orders ) ], IsPrimeInt );
    for p in primes do
      PowerMap( gaptbl, p );
    od;
    for p in primes do
      result.powerinfo[p]:= MOCPowerInfo( extendedfields, gal,
                                          ComputedPowerMaps( gaptbl ), p );
    od;

    # '30900': here all irreducible characters
    result.30900:= MOCChars( result, Irr( gaptbl ) );

    return result;
end );


#############################################################################
##
#F  MOCTableP( <gaptbl>, <basicset> )
##
##  MOC 3 format table of GAP Brauer table <gaptbl>,
##  with basic set of ordinary irreducibles at positions in
##  'Irr( OrdinaryCharacterTable( <gaptbl> ) )' given in the list <basicset>
##
BindGlobal( "MOCTableP", function( gaptbl, basicset )
    local i, j, p, result, fusion, mocfusion, images, ordinary, fld, pblock,
          invpblock, ppart, ord, degrees, defect, deg, charfusion, pos,
          repcycsub, ncharsperblock, restricted, invcharfusion, inf, mapp,
          gaptbl_classes;

    # check the arguments
    if not ( IsBrauerTable( gaptbl ) and IsList( basicset ) ) then
      Error( "<gaptbl> must be a Brauer character table,",
             " <basicset> must be a list" );
    fi;

    # transfer information from ordinary MOC table to 'result'
    ordinary:= MOCTable0( OrdinaryCharacterTable( gaptbl ) );
    fusion:= GetFusionMap( gaptbl, OrdinaryCharacterTable( gaptbl ) );
    images:= Set( ordinary.cycsubgps{ fusion } );

    # initialize the record
    result:= rec( identifier := Concatenation( "MOCTable(",
                                               Identifier( gaptbl ), ")" ),
                  prime  := UnderlyingCharacteristic( gaptbl ),
                  fields := [],
                  ordinary:= ordinary,
                  GAPtbl := gaptbl );

    result.cycsubgps:= List( fusion,
                   x -> Position( images, ordinary.cycsubgps[x] ) );
    repcycsub:= ProjectionMap( result.cycsubgps );
    result.repcycsub:= repcycsub;

    mocfusion:= CompositionMaps( ordinary.cycsubgps, fusion );

    # fusion map to restrict characters from 'ordinary' to 'result'
    charfusion:= [];
    pos:= 1;
    for i in [ 1 .. Length( result.cycsubgps ) ] do
      Add( charfusion, pos );
      pos:= pos + 1;
      while pos <= NrConjugacyClasses( result.ordinary.GAPtbl ) and
            OrdersClassRepresentatives( result.ordinary.GAPtbl )[ pos ]
                mod result.prime = 0 do
        pos:= pos + 1;
      od;
    od;

    result.fusions:= [ rec( name:= ordinary.identifier, map:= charfusion ) ];
    invcharfusion:= InverseMap( charfusion );

    result.galconjinfo:= [];
    for i in fusion do
      Append( result.galconjinfo,
              [ Position( images, ordinary.galconjinfo[ 2*i-1 ] ),
                ordinary.galconjinfo[ 2*i ] ] );
    od;

    for fld in [ "centralizers", "orders", "fieldbases", "30170",
                 "tensinfo", "invmap" ] do
      result.( fld ):= List( result.repcycsub,
                             i -> ordinary.( fld )[ mocfusion[i] ] );
    od;

    mapp:= InverseMap( CompositionMaps( ordinary.cycsubgps,
               CompositionMaps( charfusion,
                   InverseMap( result.cycsubgps ) ) ) );
    for i in [ 2 .. Length( result.30170 ) ] do
      for j in 2 * [ 1 .. Length( result.30170[i] ) / 2 ] - 1 do
        result.30170[i][j]:= mapp[ result.30170[i][j] ];
      od;
    od;


    result.powerinfo:= [];
    for p in Filtered( [ 2 .. Maximum( ordinary.orders ) ], IsPrimeInt ) do

      inf:= List( result.repcycsub,
                  i -> ordinary.powerinfo[p][ mocfusion[i] ] );
      for i in [ 1 .. Length( inf ) ] do
        pos:= 2;
        while pos < Length( inf[i] ) do
          while inf[i][ pos + 1 ] <> 0 do
            inf[i][ pos ]:= invcharfusion[ inf[i][ pos ] ];
            pos:= pos + 2;
          od;
          inf[i][ pos ]:= invcharfusion[ inf[i][ pos ] ];
          pos:= pos + 3;
        od;
      od;
      result.powerinfo[p]:= inf;

    od;

    # '30310' number of $p$-blocks
    pblock:= PrimeBlocks( OrdinaryCharacterTable( gaptbl ),
                          result.prime ).block;
    invpblock:= InverseMap( pblock );
    for i in [ 1 .. Length( invpblock ) ] do
      if IsInt( invpblock[i] ) then
        invpblock[i]:= [ invpblock[i] ];
      fi;
    od;
    result.30310:= Maximum( pblock );

    # '30320' defect, numbers of ordinary and modular characters per block
    result.30320:= [ ];
    ppart:= 0;
    ord:= Size( gaptbl );
    while ord mod result.prime = 0 do
      ppart:= ppart + 1;
      ord:= ord / result.prime;
    od;

    for i in [ 1 .. Length( invpblock ) ] do
      defect:= result.prime ^ ppart;
      for j in invpblock[i] do
        deg:= Irr( OrdinaryCharacterTable( gaptbl ) )[j][1];
        while deg mod defect <> 0 do
          defect:= defect / result.prime;
        od;
      od;
      restricted:= List( Irr( OrdinaryCharacterTable( gaptbl )
                         ){ invpblock[i] },
                         x -> x{ fusion } );

      # Form the scalar product on $p$-regular classes.
      gaptbl_classes:= SizesConjugacyClasses( gaptbl );
      ncharsperblock:= Sum( restricted,
          y -> Sum( [ 1 .. Length( gaptbl_classes ) ],
                    i -> gaptbl_classes[i] * y[i]
                             * GaloisCyc( y[i], -1 ) ) ) / Size( gaptbl );

      Add( result.30320,
           [ ppart - Length( FactorsInt( defect ) ),
             Length( invpblock[i] ),
             ncharsperblock ] );
    od;

    # '30350' distribution of ordinary irreducibles to blocks
    #         (irreducible character number 'i' has number 'i')
    result.30350:= List( invpblock, ShallowCopy);

    # '30360' distribution of basic set characters to blocks:
    result.30360:= List( invpblock,
                         x -> List( Intersection( x, basicset ),
                                    y -> Position( basicset, y ) ) );

    # '30370' positions of basic set characters in irreducibles (per block)
    result.30370:= List( invpblock, x -> Intersection( x, basicset ) );

    # '30550' decomposition of ordinary irreducibles in basic set
    basicset:= Irr( ordinary.GAPtbl ){ basicset };
    basicset:= MOCChars( result, List( basicset, x -> x{ fusion } ) );
    result.30550:= DecompositionInt( basicset,
                          List( ordinary.30900, x -> x{ charfusion } ), 30 );

    # '30900' basic set of restricted ordinary irreducibles,
    result.30900:= basicset;

    return result;
end );


#############################################################################
##
#F  MOCTable( <ordtbl> )
#F  MOCTable( <modtbl>, <basicset> )
##
InstallGlobalFunction( MOCTable, function( arg )
    if Length( arg ) = 1 and IsOrdinaryTable( arg[1] ) then
      return MOCTable0( arg[1] );
    elif Length( arg ) = 2 and IsBrauerTable( arg[1] )
                           and IsList( arg[2] ) then
      return MOCTableP( arg[1], arg[2] );
    else
      Error( "usage: MOCTable( <ordtbl> ) resp.",
                   " MOCTable( <modtbl>, <basicset> )" );
    fi;
end );


#############################################################################
##
#F  MOCString( <moctbl>[, <chars>] )
##
InstallGlobalFunction( MOCString, function( arg )
    local str,                     # result string
          i, j, d, p,              # loop variables
          tbl,                     # first argument
          ncol, free,              # number of columns for printing
          lettP, lettN, digit,     # lists of letters for encoding
          Pr, PrintNumber,         # local functions for printing
          trans, gal,
          repcycsub,
          ord,                     # corresponding ordinary table
          fus, invfus,             # transfer between ord. and modular table
          restr,                   # restricted ordinary irreducibles
          basicset, BS,            # numbers in basic set, basic set itself
          aut, gallist, fields,
          F,
          pow, im, cl,
          info, chi,
          dec;

    # 1. Preliminaries:
    #    initialisations, local functions needed for encoding and printing
    str:= "";

    # number of columns for printing
    ncol:= 80;
    free:= ncol;

    # encode numbers in '[ -9 .. 9 ]' as letters
    lettP:= "abcdefghij";
    lettN:= "klmnopqrs";
    digit:= "0123456789";

    # local function 'Pr':
    # Append 'string' in lines of length 'ncol'
    Pr:= function( string )
    local len;
    len:= Length( string );
    if len <= free then
      Append( str, string );
      free:= free - len;
    else
      if 0 < free then
        Append( str, string{ [ 1 .. free ] } );
        string:= string{ [ free+1 .. len ] };
      fi;
      Append( str, "\n" );
      for i in [ 1 .. Int( ( len - free ) / ncol ) ] do
        Append( str, string{ [ 1 .. ncol ] }, "\n" );
        string:= string{ [ ncol+1 .. Length( string ) ] };
      od;
      free:= ncol - Length( string );
      if free <> ncol then
        Append( str, string );
      fi;
    fi;
    end;

    # local function 'PrintNumber': print MOC3 code of number 'number'
    PrintNumber:= function( number )
    local i, sumber, sumber1, sumber2, len, rest;
    sumber:= String( AbsInt( number ) );
    len:= Length( sumber );
    if len > 4 then

      # long number, fill with leading zeros
      rest:= len mod 4;
      if rest = 0 then
        rest:= 4;
      fi;
      for i in [ 1 .. 4-rest ] do
        sumber:= Concatenation( "0", sumber );
        len:= len+1;
      od;

      sumber1:= sumber{ [ 1 .. len - 4 ] };
      sumber2:= sumber{ [ len - 3 .. len ] };

      # code of last digits is always 'vABCD' or 'wABCD'
      if number >= 0 then
        sumber:= Concatenation( sumber1, "v", sumber2 );
      else
        sumber:= Concatenation( sumber1, "w", sumber2 );
      fi;

    else

      # short numbers (up to 9999), encode the last digits
      if len = 1 then
        if number >= 0 then
          sumber:= [ lettP[ Position( digit, sumber[1] )     ] ];
        else
          sumber:= [ lettN[ Position( digit, sumber[1] ) - 1 ] ];
        fi;
      elif len = 2 then
        if number >= 0 then
          sumber:= Concatenation( "t", sumber );
        else
          sumber:= Concatenation( "u", sumber );
        fi;
      elif len = 3 then
        if number >= 0 then
          sumber:= Concatenation( "v0", sumber );
        else
          sumber:= Concatenation( "w0", sumber );
        fi;
      else
        if number >= 0 then
          sumber:= Concatenation( "v", sumber );
        else
          sumber:= Concatenation( "w", sumber );
        fi;
      fi;
    fi;

    # print the code in lines of length 'ncol'
    Pr( sumber );
    end;

    if Length( arg ) = 1 and IsMatrix( arg[1] ) then

      # number of columns
      Pr( "y110" );
      PrintNumber( Length( arg[1] ) );
      PrintNumber( Length( arg[1] ) );

      # matrix entries under label '30900'
      Pr( "y900" );
      for i in arg[1] do
        for j in i do
          PrintNumber( j );
        od;
      od;

      Pr( "z" );

    elif not ( Length( arg ) in [ 1, 2 ] and IsRecord( arg[1] ) and
             ( Length( arg ) = 1 or IsList( arg[2] ) ) ) then
      Error( "usage: MOCString( <moctbl>[, <chars>] )" );
    else

      tbl:= arg[1];

      # '30100' start of the table
      Pr( "y100" );

      # '30105' characteristic of the field
      Pr( "y105" );
      PrintNumber( tbl.prime );

      # '30110' number of p-regular classes and of cyclic subgroups
      Pr( "y110" );
      PrintNumber( Length( SizesCentralizers( tbl.GAPtbl ) ) );
      PrintNumber( Length( tbl.centralizers ) );

      # '30130' centralizer orders
      Pr( "y130" );
      for i in tbl.centralizers do PrintNumber( i ); od;

      # '30140' representative orders of cyclic subgroups
      Pr( "y140" );
      for i in tbl.orders do PrintNumber( i ); od;

      # '30150' field information
      Pr( "y150" );

      # loop over cyclic subgroups
      for i in tbl.fieldbases do
        if UnderlyingLeftModule( i ) = Rationals then
          PrintNumber( 1 );
        else
          F:= MOCFieldInfo( UnderlyingLeftModule( i ) );
          PrintNumber( F.nofcyc );           # $\Q(e_N)$ is the conductor
          PrintNumber( Length( F.repres ) ); # degree of the field
          for j in F.repres do
            PrintNumber( j );                # representatives of the orbits
          od;
          PrintNumber( Length( F.stabil ) ); # no. generators for stabilizer
          for j in F.stabil do
            PrintNumber( j );                # generators for stabilizer
          od;
        fi;
      od;

      # '30160' galconjinfo of classes:
      Pr( "y160" );
      for i in tbl.galconjinfo do PrintNumber( i ); od;

      # '30170' power maps
      Pr( "y170" );
      for i in Flat( tbl.30170 ) do PrintNumber( i ); od;

      # '30210' tensor product information
      Pr( "y210" );
      for i in Flat( tbl.tensinfo ) do PrintNumber( i ); od;

      # '30220' inverse map (to compute complex conjugate characters)
      Pr( "y220" );
      for i in Flat( tbl.invmap ) do PrintNumber( i ); od;

      # '30230' power map (field embeddings for $p$-th symmetrizations,
      # where $p$ is a prime not larger than the maximal element order);
      # note that the necessary power maps must be stored on 'tbl'
      Pr( "y230" );
      for p in [ 1 .. Length( tbl.powerinfo ) - 1 ] do
        if IsBound( tbl.powerinfo[p] ) then
          PrintNumber( p );
          for j in Flat( tbl.powerinfo[p] ) do PrintNumber( j ); od;
          Pr( "y050" );
        fi;
      od;
      # no '30050' at the end!
      p:= Length( tbl.powerinfo );
      PrintNumber( p );
      for j in Flat( tbl.powerinfo[p] ) do PrintNumber( j ); od;

      # '30310' number of p-blocks
      if IsBound( tbl.30310 ) then
        Pr( "y310" );
        PrintNumber( tbl.30310 );
      fi;

      # '30320' defect, number of ordinary and modular characters per block
      if IsBound( tbl.30320 ) then
        Pr( "y320" );
        for i in tbl.30320 do
          PrintNumber( i[1] );
          PrintNumber( i[2] );
          PrintNumber( i[3] );
          Pr( "y050" );
        od;
      fi;

      # '30350' relative numbers of ordinary characters per block
      if IsBound( tbl.30350 ) then
        Pr( "y350" );
        for i in tbl.30350 do
          for j in i do PrintNumber( j ); od;
          Pr( "y050" );
        od;
      fi;

      # '30360' distribution of basic set characters to blocks:
      #         relative numbers in the basic set
      if IsBound( tbl.30360 ) then
        Pr( "y360" );
        for i in tbl.30360 do
          for j in i do PrintNumber( j ); od;
          Pr( "y050" );
        od;
      fi;

      # '30370' relative numbers of basic set characters (blockwise)
      if IsBound( tbl.30370 ) then
        Pr( "y370" );
        for i in tbl.30370 do
          for j in i do PrintNumber( j ); od;
          Pr( "y050" );
        od;
      fi;

      # '30500' matrices of scalar products of Brauer characters with PS
      #         (per block)
      if IsBound( tbl.30500 ) then
        Pr( "y700" );
        for i in tbl.30700 do
          for j in Concatenation( i ) do PrintNumber( j ); od;
          Pr( "y050" );
        od;
      fi;

      # '30510' absolute numbers of '30500' characters
      if IsBound( tbl.30510 ) then
        Pr( "y510" );
        for i in tbl.30510 do PrintNumber( i ); od;
      fi;

      # '30550' decomposition of ordinary characters into basic set
      if IsBound( tbl.30550 ) then
        Pr( "y550" );
        for i in Concatenation( tbl.30550 ) do
          PrintNumber( i );
        od;
      fi;

      # '30590' ??
      # '30690' ??

      # '30700' matrices of scalar products of PS with BS (per block)
      if IsBound( tbl.30700 ) then
        Pr( "y700" );
        for i in tbl.30700 do
          for j in Concatenation( i ) do PrintNumber( j ); od;
          Pr( "y050" );
        od;
      fi;

      # '30710'
      if IsBound( tbl.30710 ) then
        Pr( "y710" );
        for i in tbl.30710 do PrintNumber( i ); od;
      fi;

      # '30900' basic set of restricted ordinary irreducibles,
      #         or characters in <chars>
      Pr( "y900" );
      if Length( arg ) = 2 then

        # case 'MOCString( <tbl>, <chars> )'
        for chi in arg[2] do
          for i in chi do PrintNumber( i ); od;
        od;

      elif IsBound( tbl.30900 ) then

        # case 'MOCString( <tbl> )'
        for i in Concatenation( tbl.30900 ) do PrintNumber( i ); od;

      fi;

      # '31000' end of table
      Pr( "z\n" );

    fi;

    # Return the result.
    return str;
end );


#############################################################################
##
##  3. interface to GAP 3
##


#############################################################################
##
#V  GAP3CharacterTableData
##
##  The pair '[ "group", "UnderlyingGroup" ]' is not contained in the list
##  because GAP 4 expects that together with the group, conjugacy classes
##  are stored compatibly with the ordering of columns in the table;
##  in GAP 3, conjugacy classes were not supported as a part of character
##  tables.
##
InstallValue( GAP3CharacterTableData, [
    [ "automorphisms", "AutomorphismsOfTable" ],
    [ "centralizers", "SizesCentralizers" ],
    [ "classes", "SizesConjugacyClasses" ],
    [ "fusions", "ComputedClassFusions" ],
    [ "fusionsources", "NamesOfFusionSources" ],
    [ "identifier", "Identifier" ],
    [ "irreducibles", "Irr" ],
    [ "name", "Name" ],
    [ "orders", "OrdersClassRepresentatives" ],
    [ "permutation", "ClassPermutation" ],
    [ "powermap", "ComputedPowerMaps" ],
    [ "size", "Size" ],
    [ "text", "InfoText" ],
    ] );


#############################################################################
##
#F  GAP3CharacterTableScan( <string> )
##
InstallGlobalFunction( GAP3CharacterTableScan, function( string )
    local gap3table, gap4table, pair;

    # Remove the substring '\\\n', which may split component names.
    string:= ReplacedString( string, "\\\n", "" );

    # Remove the variable name 'CharTableOps', which GAP 4 does not know.
    string:= ReplacedString( string, "CharTableOps", "0" );

    # Get the GAP 3 record encoded by the string.
    gap3table:= EvalString( string );

    # Fill the GAP 4 record.
    gap4table:= rec( UnderlyingCharacteristic:= 0 );
    for pair in GAP3CharacterTableData do
      if IsBound( gap3table.( pair[1] ) ) then
        gap4table.( pair[2] ):= gap3table.( pair[1] );
      fi;
    od;

    return ConvertToCharacterTable( gap4table );
    end );


#############################################################################
##
#F  GAP3CharacterTableString( <tbl> )
##
InstallGlobalFunction( GAP3CharacterTableString, function( tbl )
    local str, pair, val;

    str:= "rec(\n";
    for pair in GAP3CharacterTableData do
      if Tester( ValueGlobal( pair[2] ) )( tbl ) then
        val:= ValueGlobal( pair[2] )( tbl );
        Append( str, pair[1] );
        Append( str, " := " );
        if pair[1] in [ "name", "text", "identifier" ] then
          Append( str, "\"" );
          Append( str, String( val ) );
          Append( str, "\"" );
        elif pair[1] = "irreducibles" then
          Append( str, "[\n" );
          Append( str, JoinStringsWithSeparator(
              List( val, chi -> String( ValuesOfClassFunction( chi ) ) ),
              ",\n" ) );
          Append( str, "\n]" );
        elif pair[1] = "automorphisms" then
          # There is no 'String' method for groups.
          Append( str, "Group( " );
          Append( str, String( GeneratorsOfGroup( val ) ) );
          Append( str, ", () )" );
        else
#T what about "cliffordTable"?
#T (special function 'PrintCliffordTable' in GAP 3)
          Append( str, String( val ) );
        fi;
        Append( str, ",\n" );
      fi;
    od;
    Append( str, "operations := CharTableOps )\n" );

    return str;
    end );


#############################################################################
##
##  4. interface to the Cambridge format
##


#############################################################################
##
#F  CTblLib.GalConj( <n>, <N>, <k> )
##
##  returns the value <M>k'</M> defined in
##  Chapter 7, Section 19 of the &ATLAS; of Finite Groups
##  (but be careful what exactly is said there).
##  This value is used to construct follower characters and classes.
##  <P/>
##  <A>N</A>, <A>n</A> and <A>k</A> are as in the &ATLAS;,
##  and <M>k'</M> is congruent <M>1</M> mod <M>N / ( 2part \* 3part )</M>,
##  congruent <M>\pm 1</M> mod '2part' and congruent <M>\pm 1</M>
##  mod '3part'.
##  This yields three nontrivial solutions mod <A>N</A>,
##  we consider only those with <M>k'</M> congruent <A>k</A> mod <A>n</A>,
##  and if there is an ambiguity left,
##  we prefer the solution congruent <M>+1</M>.
##  (R. Parker says: <Q>+1 is better than -1.</Q>)
##  <P/>
##  Note that <A>n</A> must lie in <M>[ 3, 4, 5, 6, 12 ]</M>.
##  Also note that the &ATLAS; of Brauer Characters defines
##  another convention for follower cohorts of characters.
##
CTblLib.GalConj:= function( n, N, k )
    local i, j, facts, pos, 2part, 3part, 5part, a, b, c, d, kprime,
          g, gcd, gcd2;

    if N = 1 or k = 1 then
      return 1;
    elif n = 5 then
      5part:= 1;
      for i in FactorsInt( N ) do
        if i = 5 then
          5part:= 5 * 5part;
        fi;
      od;

      for kprime in List( [ 0 .. Int( N-1-k / 5 ) ], x -> k + x * 5 ) do
        if Gcd( kprime, N ) = 1 and ( kprime - 1 ) mod ( N / 5part ) = 0
           and ( kprime^4 - 1 ) mod 5part = 0 then
          return kprime mod N;
        fi;
      od;
    elif 12 mod n = 0 then
      facts:= FactorsInt( N );
      2part:= 1;
      3part:= 1;
      for i in facts do
        if i = 2 then
          2part:= 2 * 2part;
        elif i = 3 then
          3part:= 3 * 3part;
        fi;
      od;

      # x congr. u (mod M) and x congr. v (mod N)
      # with 1 = Gcd( M, N ) = a * M + b * N
      # yields the solution x = u * b * N + v * a * M (mod M * N)
      gcd:= Gcdex( N / 3part, 3part );
      c:= gcd.coeff1; d:= gcd.coeff2;
      kprime:= ( d * 3part - c * N / 3part );   #  1 ( mod N / 3part )
                                                # -1 ( mod 3part )
      if ( kprime - k ) mod n = 0 then
        return kprime mod N;
      else
        gcd2:= Gcdex( N / ( 2part * 3part ), 2part );
        a:= gcd2.coeff1;
        b:= gcd2.coeff2;
        g:= b * 2part - a * N / ( 2part * 3part );    #  g ( mod N / 3part )
                                                      # -1 ( mod 2part )
        kprime:= ( g * d * 3part + c * N / 3part );   #  1 ( mod 3part )
        if ( kprime - k ) mod n = 0 then
          return kprime mod N;
        else
          kprime:= ( g * d * 3part - c * N / 3part );  # -1 ( mod 3part )
                                                       # -1 ( mod 2part )
          if ( kprime - k ) mod n = 0 then
            return kprime mod N;
          else
            Error( "GalConj(n,N,k) with n=", n, " N=", N, " k=", k );
          fi;
        fi;
      fi;
    else
      Error( "CTblLib.GalConj is implemented only for ",
             "n in [ 2, 3, 4, 5, 6, 12 ]" );
    fi;
    end;


#############################################################################
##
#F  CTblLib.CommonCambridgeMaps( <tbls> )
##
##  We assume that
##  - <tbls> is a dense list that describes the tables of
##    cyclic upward extensions of a simple group,
##  - the first entry is the table of this simple group,
##  - the tables occur in the same order as in the &ATLAS;, and
##  - inner classes precede outer ones, as in the &ATLAS;.
##
CTblLib.CommonCambridgeMaps:= function( tbls )
    local alpha, lalpha, name, tbl, char, names, power, prime, choice,
          number, letters, i, orders, galois, n, lin, root, outerclasses,
          fus, follower, j, k, ord, nam, dashes, pos, tr, galoismat, inverse,
          stabilizers, entry, family, residues, n0, resorders, aut, p, div,
          gcd, po, img, found, subtbl, subfus;

    alpha:= [ "A","B","C","D","E","F","G","H","I","J","K","L","M",
              "N","O","P","Q","R","S","T","U","V","W","X","Y","Z" ];
    lalpha:= Length( alpha );

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

    tbl:= tbls[1];
    char:= UnderlyingCharacteristic( tbl );

    names:= [];
    power:= [];
    prime:= [];
    choice:= [];
    number:= [];
    letters:= [];

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

      names[i]:= [];
      power[i]:= [];
      prime[i]:= [];
      choice[i]:= [];

      if tbls[i] <> fail and
         ( char = 0 or ( Size( tbls[i] ) / Size( tbl ) ) mod char <> 0 ) then

        # Compute absolute names for the outer classes.
        orders:= OrdersClassRepresentatives( tbls[i] );
        galois:= GaloisMat( TransposedMat( Irr( tbls[i] ) ) ).galoisfams;

        if i = 1 then
          n:= NrConjugacyClasses( tbl );
          lin:= [ ListWithIdenticalEntries( n, 1 ) ];
          root:= 1;
          choice[i]:= [ 1 .. n ];
          outerclasses:= [ 1 .. n ];
        else
          fus:= GetFusionMap( tbl, tbls[i] );
          if fus = fail then
            Error( "fusion tbls[1] -> tbls[", i, "] is not stored" );
          fi;
          lin:= Filtered( Irr( tbls[i] ),
                          x -> x[1] = 1 and Set( x{ fus } ) = [ 1 ] );
          n:= Length( lin );
          if not n in [ 2 .. 6 ] then
            Error( "only cyclic upwards extensions by [ 2, 3, 4, 5, 6 ] ",
                   "are supported" );
          fi;
          lin:= First( lin, x -> E(n) in x );
          outerclasses:= PositionsProperty( lin, x -> Order( x ) = n );
          root:= lin[ outerclasses[1] ];
          choice[i]:= Positions( lin, root );
          if n <> 2 then
            follower:= [];
            for j in choice[i] do
              follower[j]:= [];
              for k in PrimeResidues( n ) do
                if k <> 1 then
                  follower[j][k]:= CTblLib.GalConj( n, orders[j], k );
                fi;
              od;
            od;
          fi;
        fi;

        for j in choice[i] do
          ord:= orders[j];
          if not IsBound( number[ ord ] ) then
            number[ ord ]:= 1;
          fi;
          nam:= Concatenation( String( ord ), name( number[ ord ] ) );
          number[ ord ]:= number[ ord ] + 1;
          names[i][j]:= nam;
          if not IsInt( root ) then
            dashes:= "'";
            for k in follower[j] do
              pos:= PowerMap( tbls[i], k )[j];
              if IsBound( names[i][ pos ] ) then
                Error( "names[i][ pos ] is bound, this should not happen!" );
              fi;
              names[i][ pos ]:= Concatenation( nam, dashes );
              Add( dashes, '\'' );
            od;
          fi;
        od;

        # Compute relative class names.
        # Note that the relative names for non-leading classes in a family
        # of algebraically conjugate classes are chosen only if the classes
        # of the family are consecutive.
        tr:= TransposedMat( Irr( tbls[i] ) );
        galoismat:= GaloisMat( tr );
        galois:= galoismat.galoisfams;
        inverse:= InverseClasses( tbls[i] );
        letters[i]:= List( names[i],
          x -> x{ [ PositionProperty( x, IsAlphaChar ) .. Length( x ) ] } );

        stabilizers:= [];
        for j in [ 1 .. Length( galois ) ] do
          if IsList( galois[j] ) then
            entry:= galois[j][1][1];
            stabilizers[j]:= Filtered( PrimeResidues( orders[ entry ] ),
                x -> GaloisCyc( tr[ entry ], x ) = tr[ entry ] );
          fi;
        od;

        for j in outerclasses do
          # Adjust class names for consecutive families of alg. conjugates.
          if IsList( galois[j] ) then
            family:= SortedList( galois[j][1] );
            n:= orders[ galois[j][1][1] ];
            residues:= PrimeResidues( n );
            n0:= Conductor( tr[ galois[j][1][1] ] );
            if not ( n0 = 9 and Length( family ) = 3  ) then
              # (For 'y9' type irrationalities, use '*2' and '*4'.)
              resorders:= List( residues, x -> OrderMod( x, n ) );
              SortParallel( resorders, residues );
            fi;

            if family = [ family[1] .. family[ Length( family ) ] ] then
              for k in [ 2 .. Length( galois[j][1] ) ] do
                if not '\'' in names[i][ galois[j][1][k] ] then
                  if galois[j][1][k] = inverse[j] then
                    # Use '**' whenever this is possible.
                    aut:= "**";
                  elif Length( galois[j][1] ) = 2 * Phi( Order( root ) ) then
                    # Use '*' in real quadratic cases, or if at least the
                    # restriction to proxy classes/characters is quadratic.
                    aut:= "*";
                  else
                    # The powering computed by 'GaloisMat' refers to the
                    # conductor of the character values on the class,
                    # it need not be coprime to the element order;
                    # rewrite this.
                    aut:= galois[j][2][k] mod n;
                    if Gcd( aut, n ) <> 1 then
                      aut:= aut mod n0;
                      aut:= First( residues, x -> x mod n0 = aut );
                      if aut = fail then
                        Error( "problem with prime residues!" );
                      fi;
                    fi;

                    # Choose '*k' with 'k' of smallest possible mult. order.
                    # (Note that 'residues' is ordered accordingly.)
                    aut:= First( residues,
                                 x -> ( x / aut ) mod n in stabilizers[j] );

                  fi;

                  if IsString( aut ) then
                    names[i][ galois[j][1][k] ]:=
                        Concatenation( letters[i][ galois[j][1][k] ], aut );
                  else
                    # We have no negative 'aut' yet.
                    names[i][ galois[j][1][k] ]:= aut;
                  fi;
                fi;
              od;

              # Adjust the names such that '**k' occur if applicable.
              # If ** occurs, then together with *k also **k occurs,
              # except if this string is longer than *(n-k)
              if inverse[ family[1] ] <> family[1] then
                # Run over the pairs of complex conjugate classes,
                # except the leading pair.
                for k in family do
                  if k < inverse[k] and IsInt( names[i][k] ) then
                    if names[i][k] < names[i][ inverse[k] ] and
                       Length( String( names[i][k] ) ) + 1
                       <= Length( String( names[i][ inverse[k] ] ) ) then
                      names[i][ inverse[k] ]:= - names[i][k];
                    elif names[i][k] > names[i][ inverse[k] ] and
                       Length( String( names[i][ inverse[k] ] ) ) + 1
                       <= Length( String( names[i][k] ) ) then
                      names[i][k]:= - names[i][ inverse[k] ];
                    fi;
                  fi;
                od;
              fi;

              # Finally set the relative class names.
              for k in [ 2 .. Length( galois[j][1] ) ] do
                entry:= galois[j][1][k];
                if IsInt( names[i][ entry ] ) then
                  aut:= names[i][ entry ];
                  if 0 < aut then
                    names[i][ entry ]:= Concatenation( letters[i][ entry ],
                                            "*", String( aut ) );
                  else
                    names[i][ entry ]:= Concatenation( letters[i][ entry ],
                                            "**", String( -aut ) );
                  fi;
                fi;
              od;

            fi;
          fi;
        od;

        # Deal with the lines for power maps and p' part.
        for j in choice[i] do
          power[i][j]:= "";
          prime[i][j]:= "";
          if orders[j] <> 1 then
            for p in PrimeDivisors( orders[j] ) do
              div:= orders[j];
              while div mod p = 0 do
                div:= div / p;
              od;
              gcd:= Gcdex( div, orders[j] / div );
              po:= orders[j] / div * gcd.coeff2;
              if po <= 0 then
                po:= po + orders[j];
              fi;

              img:= PowerMap( tbls[i], p, j );
              if IsBound( letters[i][ img ] ) then
                Append( power[i][j], letters[i][ img ] );
              else
                found:= false;
                for k in [ 1 .. i-1 ] do
                  subtbl:= tbls[k];
                  if subtbl <> fail then
                    subfus:= GetFusionMap( subtbl, tbls[i] );
                    if subfus <> fail and img in subfus then
                      found:= true;
                      Append( power[i][j],
                              letters[k][ Position( subfus, img ) ] );
                      break;
                    fi;
                  fi;
                od;
                if not found then
                  Append( power[i][j], "?" );
                fi;
              fi;
              img:= PowerMap( tbls[i], po, j );
              if IsBound( letters[i][ img ] ) then
                Append( prime[i][j], letters[i][ img ] );
              else
                found:= false;
                for k in [ 1 .. i-1 ] do
                  subtbl:= tbls[k];
                  if subtbl <> fail then
                    subfus:= GetFusionMap( subtbl, tbls[i] );
                    if subfus <> fail and img in subfus then
                      found:= true;
                      Append( prime[i][j],
                              letters[k][ Position( subfus, img ) ] );
                      break;
                    fi;
                  fi;
                od;
                if not found then
                  Append( prime[i][j], "?" );
                fi;
              fi;
            od;
          fi;
        od;
      fi;
    od;

    # Return the result.
    return rec( power  := List( power, l -> List( l,
                                    x -> Filtered( x, y -> y <> '\'' ) ) ),
                prime  := List( prime, l -> List( l,
                                    x -> Filtered( x, y -> y <> '\'' ) ) ),
                names  := names,
                choice := choice );
end;


#############################################################################
##
#F  CambridgeMaps( <tbl> )
##
InstallGlobalFunction( CambridgeMaps, function( tbl )
    local orders,      # representative orders of 'tbl'
          classnames,  # (relative) class names in ATLAS format
          letters,     # non-order parts of 'classnames'
          galois,      # info about algebraic conjugacy
          inverse,     # positions of inverse classes
          power,       # ATLAS line for the power map
          prime,       # ATLAS line for the p' parts
          i,           # loop variable
--> --------------------

--> maximum size reached

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

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