home *** CD-ROM | disk | FTP | other *** search
/ Math Solutions 1995 October / Math_Solutions_CD-ROM_Walnut_Creek_October_1995.iso / pc / mac / discrete / lib / ctgeneri.g < prev    next >
Encoding:
Text File  |  1993-05-05  |  40.0 KB  |  1,061 lines

  1. #############################################################################
  2. ##
  3. #A  ctgeneri.g                  GAP library                     Oliver Bonten
  4. #A                                                            & Thomas Breuer
  5. ##
  6. #A  @(#)$Id: ctgeneri.g,v 3.28 1993/02/11 11:02:14 sam Rel $
  7. ##
  8. #Y  Copyright 1990-1992,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
  9. ##
  10. ##  This file contains  those  functions  that  construct character tables.
  11. ##
  12. #H  $Log: ctgeneri.g,v $
  13. #H  Revision 3.28  1993/02/11  11:02:14  sam
  14. #H  necessary change in 'CharTableNormalSubgroup'
  15. #H
  16. #H  Revision 3.0  1991/09/03  14:15:05  goetz
  17. #H  Initial Revision.
  18. #H
  19. ##
  20.  
  21.  
  22. #############################################################################
  23. ##
  24. #F  InfoCharTable1( ... ) . . . info function for the character table package
  25. #F  InfoCharTable2( ... ) . . . info function for the character table package
  26. ##
  27.     if not IsBound( InfoCharTable1 )  then InfoCharTable1 := Ignore;  fi;
  28.     if not IsBound( InfoCharTable2 )  then InfoCharTable2 := Ignore;  fi;
  29.  
  30.  
  31. #############################################################################
  32. ##
  33. #F  CharTableRegular( <tbl>, <p> ) .  table consisting of <p>-regular classes
  34. ##
  35. CharTableRegular := function( tbl, prime )
  36.     local i, regular, fusion, inverse;
  37.     fusion:= [];
  38.     inverse:= [];
  39.     for i in [ 1 .. Length( tbl.orders ) ] do
  40.       if tbl.orders[i] mod prime <> 0 then
  41.         Add( fusion, i );
  42.         inverse[i]:= Length( fusion );
  43.       fi;
  44.     od;
  45.  
  46.     regular:= rec( name:= ConcatenationString( "Regular(", tbl.name,
  47.                                                 ",", String( prime ), ")" ),
  48.                    prime:= prime,
  49.                    order:= tbl.order,
  50.                    orders:= CompositionMaps( tbl.orders, fusion ),
  51.                    centralizers:= CompositionMaps( tbl.centralizers, fusion ),
  52.                    powermap:= [],
  53.                    fusions:= [],
  54.                    ordinary:= tbl,
  55.                    operations:= CharTableOps );
  56.  
  57.     StoreFusion( regular, tbl, rec( map:= fusion, type:= "choice" ) );
  58.     for i in [ 1 .. Length( tbl.powermap ) ] do
  59.       if IsBound( tbl.powermap[i] ) then
  60.         regular.powermap[i]:= CompositionMaps( inverse,
  61.                                 CompositionMaps( tbl.powermap[i], fusion ) );
  62.       fi;
  63.     od;
  64.     
  65.     InitClassesCharTable( regular );
  66.     
  67.     return regular;
  68.     end;
  69.  
  70.  
  71. #############################################################################
  72. ##
  73. #F  CharTableDirectProduct( <tbl1>, <tbl2> )
  74. ##
  75. ##  table of the direct product;
  76. ##
  77. ##  Note\: The table of the direct product will contain all $p$-th powermaps
  78. ##         for which $p$-th powermaps are stored on both tables.
  79. ##         Additionally, if the $p$-th powermap is stored on one table, and
  80. ##         $p$ does not divide the order of the other table, 'Powermap' is
  81. ##         called, and if the map is uniquely determined, the table of the
  82. ##         direct product will contain the $p$-th powermap, too.
  83. ##
  84. CharTableDirectProduct := function( tbl1, tbl2 )
  85.     local i, j, k, direct, ncc1, ncc2, orders1, orders2, orders1_i, orders,
  86.           fus, powermap_k, powermap1_k, powermap2_k, ncc2_i, pow;
  87.     
  88.     if not IsCharTable( tbl1 ) or not IsCharTable( tbl2 ) then
  89.       Error( "arguments must be character tables" );
  90.     fi;
  91.     
  92.     direct:= rec( order:= tbl1.order * tbl2.order,
  93.                   name:= ConcatenationString( tbl1.name, "x", tbl2.name ),
  94.                   centralizers:= KroneckerProduct( [ tbl1.centralizers ],
  95.                                          [ tbl2.centralizers ] )[1]      );
  96.  
  97.     ncc1:= Length( tbl1.centralizers );
  98.     ncc2:= Length( tbl2.centralizers );
  99.  
  100.     # class parameters, if present in both tables
  101.     if IsBound( tbl1.classparam ) and IsBound( tbl2.classparam ) then
  102.       direct.classparam:= [];
  103.       for i in [ 1 .. ncc1 ] do
  104.         for j in [ 1 .. ncc2 ] do
  105.           direct.classparam[ j + ncc2 * ( i - 1 ) ]:=
  106.                        [ tbl1.classparam[i], tbl2.classparam[j] ];
  107.         od;
  108.       od;
  109.     fi;
  110.  
  111.     # element orders, if present in both tables
  112.     if IsBound( tbl1.orders ) and IsBound( tbl2.orders ) then
  113.       orders1:= tbl1.orders;
  114.       orders2:= tbl2.orders;
  115.       orders:= [];
  116.       for i in [ 1 .. ncc1 ] do
  117.         orders1_i:= orders1[i];
  118.         for j in [ 1 .. ncc2 ] do
  119.           orders[ j + ncc2 * ( i - 1 ) ]:= Lcm( orders1_i, orders2[j] );
  120.         od;
  121.       od;
  122.       direct.orders:= orders;
  123.     fi;
  124.     
  125.     # power maps, if present in both tables
  126.     direct.powermap:= [];
  127.     if IsBound( tbl1.powermap ) and IsBound( tbl2.powermap ) then
  128.       for k in Union( Filtered( [ 1 .. Length( tbl1.powermap ) ],
  129.                                 x -> IsBound( tbl1.powermap[x] ) ),
  130.                       Filtered( [ 1 .. Length( tbl2.powermap ) ],
  131.                                 x -> IsBound( tbl2.powermap[x] ) ) ) do
  132.     
  133.         # compute the powermap of the compound tables if the prime does
  134.         # not divide the order
  135.     
  136.         if not IsBound( tbl1.powermap[k] ) and tbl1.order mod k <> 0 then
  137.           InfoCharTable2( "#I CharTableDirectProduct: Powermap(<tbl1>,", k,
  138.                           ",rec(quick:=true) ) called\n" );
  139.           pow:= Parametrized( Powermap( tbl1, k, rec( quick:= true ) ) );
  140.           if ForAll( pow, IsInt ) then tbl1.powermap[k]:= pow; fi;
  141.         fi;
  142.         if not IsBound( tbl2.powermap[k] ) and tbl2.order mod k <> 0 then
  143.           InfoCharTable2( "#I CharTableDirectProduct: Powermap( <tbl2>, ", k,
  144.                           ",rec( quick:= true ) ) called\n" );
  145.           pow:= Parametrized( Powermap( tbl2, k, rec( quick:= true ) ) );
  146.           if ForAll( pow, IsInt ) then tbl2.powermap[k]:= pow; fi;
  147.         fi;
  148.     
  149.         if IsBound( tbl1.powermap[k] ) and IsBound( tbl2.powermap[k] ) then
  150.           powermap_k:= [];
  151.           powermap1_k:= tbl1.powermap[k];
  152.           powermap2_k:= tbl2.powermap[k];
  153.           for i in [ 1 .. ncc1 ] do
  154.             ncc2_i:= ncc2 * (i-1);
  155.             for j in [ 1 .. ncc2 ] do
  156.               powermap_k[ j + ncc2_i ]:=
  157.                         powermap2_k[j] + ncc2 * ( powermap1_k[i] - 1 );
  158.             od;
  159.           od;
  160.           direct.powermap[k]:= powermap_k;
  161.         fi;
  162.       od;
  163.     fi;
  164.  
  165.     # irreducibles and character parameters, if present in both tables
  166.     if IsBound( tbl1.irreducibles ) and IsBound( tbl2.irreducibles ) then
  167.  
  168.       direct.irreducibles:=
  169.                    KroneckerProduct( tbl1.irreducibles, tbl2.irreducibles );
  170.  
  171.       direct.irredinfo:= List( direct.irreducibles, x -> rec() );
  172.  
  173.       if IsBound( tbl1.irredinfo ) and IsBound( tbl2.irredinfo ) and
  174.          IsBound( tbl1.irredinfo[1] ) and IsBound( tbl2.irredinfo[1] ) and
  175.          IsBound( tbl1.irredinfo[1].charparam ) and
  176.          IsBound( tbl2.irredinfo[1].charparam ) then
  177.         direct.charparam:= [];
  178.         for i in [ 1 .. ncc1 ] do
  179.           for j in [ 1 .. ncc2 ] do
  180.             direct.irredinfo[ j + ncc2 * ( i - 1 ) ].charparam:=
  181.               [ tbl1.irredinfo[i].charparam, tbl2.irredinfo[j].charparam ];
  182.           od;
  183.         od;
  184.       fi;
  185.     
  186.     fi;
  187.  
  188.     # embeddings and projections
  189.     direct.fusionsource:= [];
  190.     fus:= [];
  191.     for i in [ 1 .. ncc1 ] do
  192.       for j in [ 1 .. ncc2 ] do fus[ ( i - 1 ) * ncc2 + j ]:= i; od;
  193.     od;
  194.     direct.fusions:= [ rec( name:= tbl1.name, map:= fus, type:= "factor" ) ];
  195.     fus:= [];
  196.     for i in [ 1 .. ncc1 ] do
  197.       for j in [ 1 .. ncc2 ] do fus[ ( i - 1 ) * ncc2 + j ]:= j; od;
  198.     od;
  199.     direct.fusions[2]:= rec( name:= tbl2.name, map:= fus, type:= "factor" );
  200.     fus:= [];
  201.     for i in [ 1 .. ncc1 ] do fus[i]:= ( i - 1 ) * ncc2 + 1; od;
  202.     if GetFusionMap( tbl1, direct ) <> false then
  203.       for i in [ 1 .. Length( tbl1.fusions ) ] do
  204.         if tbl1.fusions[i].name = direct.name then
  205.           if tbl1.fusions[i].map <> fus then
  206.             tbl1.fusions[i]:= rec( name:= direct.name,
  207.                                    map:= fus, type:= "normal" );
  208.             Add( direct.fusionsource, tbl1.name );
  209.             Print( "#I CharTableDirectProduct: existing subgroup fusion on",
  210.                    " <tbl1> replaced\n#I    by actual one\n" );
  211.           fi;
  212.         fi;
  213.       od;
  214.     else
  215.       StoreFusion( tbl1, direct, rec( map:= fus, type:= "normal" ) );
  216.     fi;
  217.     fus:= [];
  218.     for i in [ 1 .. ncc2 ] do fus[i]:= i; od;
  219.     if GetFusionMap( tbl2, direct ) <> false then
  220.       for i in [ 1 .. Length( tbl2.fusions ) ] do
  221.         if tbl2.fusions[i].name = direct.name then
  222.           if tbl2.fusions[i].map <> fus then
  223.             tbl2.fusions[i]:= rec( name:= direct.name,
  224.                                    map:= fus, type:= "normal" );
  225.             Add( direct.fusionsource, tbl2.name );
  226.             Print( "#I CharTableDirectProduct: existing subgroup fusion on",
  227.                    " <tbl2> replaced\n#I    by actual one\n" );
  228.           fi;
  229.         fi;
  230.       od;
  231.     else
  232.       StoreFusion( tbl2, direct, rec( map:= fus, type:= "normal" ) );
  233.     fi;
  234.     
  235.     InitClassesCharTable( direct );
  236.     direct.operations:= CharTableOps;
  237.     
  238.     return direct;
  239.     end;
  240.  
  241.  
  242. #############################################################################
  243. ##
  244. #F  CharTableFactorGroup( <tbl>, <classes_of_normal_subgroup> )
  245. ##
  246. ##  the tbl of the factor group of <tbl> with respect to the intersection
  247. ##  of kernels of those irreducibles stored on <tbl> which contain
  248. ##  <classes_of_normal_subgroups> in their kernel;
  249. ##  if all irreducible characters of <tbl> are stored, the calculated
  250. ##  normal subgroup is the normal closure of <classes_of_normal_subgroup>.
  251. ##
  252. CharTableFactorGroup := function( tbl, classes_of_normal_subgroup )
  253.     local i, j, k, x, factortable, normal_subgroup, factorirreducibles, chi,
  254.           suborder, classums, orders, facts, upper, lower, inverse, pow,
  255.           facpow, images, values, rpow, dubious, factorfusion, classsums,
  256.           factors;
  257.  
  258.     if Length( tbl.irreducibles ) < Length( tbl.irreducibles[1] ) then
  259.       Print("#I CharTableFactorGroup: Some irreducible characters of <tbl>\n",
  260.             "#I   are missing;\n",
  261.             "#I   perhaps the calculated normal subgroup will be too big.\n");
  262.     fi;
  263.  
  264.     factorirreducibles:= [];
  265.     normal_subgroup:= Set( [ 1 .. Length( tbl.centralizers ) ] );
  266.     for chi in tbl.irreducibles do
  267.       if Difference( classes_of_normal_subgroup, KernelChar(chi) ) = [] then
  268.         normal_subgroup:= Intersection( normal_subgroup, KernelChar(chi) );
  269.         Add( factorirreducibles, chi );
  270.       fi;
  271.     od;
  272.     suborder:= 0;    # the order of 'normal_subgroup'
  273.     for i in normal_subgroup do suborder:= suborder + tbl.classes[ i ]; od;
  274.     if tbl.order mod suborder <> 0 then
  275.       Error("intersection of kernels of irreducible characters containing\n",
  276.          "<classes_normal_subgroup> has an order not dividing that of <tbl>");
  277.     fi;
  278.     factortable:= rec( order:= tbl.order / suborder );
  279.     factortable.name:=
  280.              ConcatenationString( tbl.name, "modulo",
  281.                                   String( classes_of_normal_subgroup ) );
  282.     factorirreducibles:= CollapsedMat( factorirreducibles, [] );
  283.     factorfusion:= factorirreducibles.fusion;
  284.     factorirreducibles:= factorirreducibles.mat;
  285.     #
  286.     # centralizers of the factor group:
  287.     # \[ \|C_{G\|N}(gN)\| = \frac{\|G\|/\|N\|}{\|Cl_{G/N}(gN)\|}
  288.     # = \frac{\|G\|:\|N\|}{\frac{1}{\|N\|}\sum_{x fus gN} \|Cl_G(x)\|}
  289.     # = \frac{\|G\|}{\sum_{x fus gN} \|Cl_G(x)\| \]
  290.     #
  291.     classsums:= [];
  292.     for i in [ 1 .. Length( factorirreducibles[1] ) ] do
  293.       classsums[i]:= 0;
  294.     od;
  295.     for i in [ 1 .. Length( factorfusion ) ] do
  296.       classsums[ factorfusion[i] ]:=
  297.                             classsums[ factorfusion[i] ] + tbl.classes[i];
  298.     od;
  299.     factortable.centralizers:= List( classsums, x -> tbl.order / x );
  300.     if not ForAll( factortable.centralizers, IsInt ) then
  301.       Print( "#I CharTableFactorGroup: not all centralizer orders\n",
  302.              "#I    of the factor group are well defined\n" );
  303.     fi;
  304.     #
  305.     factortable.powermap:= [];
  306.     factortable.fusions:= [];
  307.     factortable.fusionsource:= [ tbl.name ];
  308.     factortable.irreducibles:= factorirreducibles;
  309.     factortable.irredinfo:= [];
  310.     if IsBound( tbl.orders ) then
  311.  
  312.       # determine representative orders up to factors of suborder:
  313.       #
  314.       # 1. the representative order is a divisor of the gcd of all
  315.       #    repres. orders of preimages (and divides 'factortable.order')
  316.       #
  317.       # 2. For all preimages $x$ of $gN$ we have
  318.       #    \[ \frac{\langle x \rangle}{\gcd( \langle x \rangle, \|N\|)}
  319.       #    \mbox{\rm divides} \|\langle gN \rangle\|, \]
  320.       #    so  \[ lcm_{x fus gN}(\frac{\|\langle x \rangle\|}{\gcd(\|\langle
  321.       #    x \rangle\|, \|N\|)}) \mbox{\rm divides} \|langle gN \rangle\|. \]
  322.       #
  323.       inverse:= InverseMap( factorfusion );
  324.       orders:= CompositionMaps( tbl.orders, inverse );
  325.       factors:= Factors( suborder );
  326.       if IsSet( orders[1] ) then
  327.         if 1 in orders[1] then
  328.           orders[1]:= 1;
  329.         else
  330.           Print("#I CharTableFactorGroup: class 1 cannot be the identity\n");
  331.         fi;
  332.       fi;
  333.       for i in [ 2 .. Length( orders ) ] do
  334.         if IsSet( orders[i] ) then
  335.           upper:= GcdInt( Iterated( orders[i], GcdInt ), factortable.order );
  336.           lower:= Lcm( List( orders[i], x -> x / GcdInt( x, suborder ) ) );
  337.         else
  338.           upper:= orders[i];
  339.           lower:= orders[i] / GcdInt( orders[i], suborder );
  340.         fi;
  341.         if upper = 1 or upper mod lower <> 0 then
  342.           Print( "#I CharTableFactorGroup: representative orders and",
  343.                  " factorfusion\n",
  344.                  "#I   are not consistent at classes ", inverse[i], "\n" );
  345.           orders[i]:= Unknown();
  346.         elif upper = lower then
  347.           orders[i]:= upper;
  348.         else
  349.           orders[i]:= Difference( lower * DivisorsInt( upper/lower ), [1] );
  350.           if Length( orders[i] ) = 1 then orders[i]:= orders[i][1]; fi;
  351.         fi;
  352.       od;
  353.     fi;
  354.     #
  355.     # if powermaps of <tbl> are known, transfer them to 'factortable':
  356.     #
  357.     if IsBound( tbl.powermap ) then
  358.       inverse:= InverseMap( factorfusion );
  359.       for i in [ 1 .. Length( inverse ) ] do
  360.         if IsInt( i ) then inverse[i]:= [ inverse[i] ]; fi;
  361.       od;
  362.       for i in [ 1 .. Length( tbl.powermap ) ] do
  363.         if IsBound( tbl.powermap[i] ) then
  364.           pow:= tbl.powermap[i];
  365.           facpow:= [];
  366.           for j in [ 1 .. Length( inverse ) ] do
  367.             # if the powermap of <tbl> is unique for all preimages
  368.             # of j all powers of the preimages must be equal;
  369.             # if there is a proper parametrized map the power of
  370.             # j is the image of the intersection of the powers
  371.             # of all preimages.
  372.             images:= Set( CompositionMaps( factorfusion,
  373.                              CompositionMaps(tbl.powermap[i],inverse[j]) ) );
  374.             if Length( images ) = 1 then
  375.               facpow[j]:= images[1];
  376.             elif ForAll( inverse[j], x -> IsInt( pow[x] ) )
  377.                  and not IsInt( images[1] ) then
  378.               Print( "#I CharTableFactorGroup: factorfusion and ", Ordinal(i),
  379.                      " powermap inconsistent at classes ", inverse[j], "\n" );
  380.               facpow[j]:= Unknown();
  381.             else
  382.               for k in [ 1 .. Length( images ) ] do
  383.                 if IsInt( images[k] ) then images[k]:= [ images[k] ]; fi;
  384.               od;
  385.               facpow[j]:= Iterated( images, Intersection );
  386.               if facpow[j] = [] then
  387.                 Print( "#I CharTableFactorGroup: factorfusion and ",
  388.                        Ordinal( i ), " powermap inconsistent at classes ",
  389.                        inverse[j], "\n" );
  390.                 facpow[j]:= Unknown(0);
  391.               fi;
  392.             fi;
  393.           od;
  394.           factortable.powermap[i]:= facpow;
  395.         fi;
  396.       od;
  397.     fi;
  398.     #
  399.     # initialize powermaps for prime divisors of 'factortable.order'
  400.     # if not yet known:
  401.     #
  402.     for i in Set( Factors( factortable.order ) ) do
  403.       if i > 1 and not IsBound( factortable.powermap[i] ) then
  404.         factortable.powermap[i]:= InitPowermap( factortable, i );
  405.         Congruences( factortable, factorirreducibles,
  406.                      factortable.powermap[i], i );
  407.       fi;
  408.     od;
  409.     if IsBound( tbl.orders ) then
  410.  
  411.       # try to improve representative orders and powermaps:
  412.       # For each prime divisor 'p' of 'factortable.order' we define a map
  413.       # 'rpow[p]' as follows:
  414.       # \['rpow[p][x]':=\left\{\begin{array}{lcl} x/p & ; & 'x mod p = 0\\
  415.       #                    x & ; & \mbox{\rm otherwise}\end{array}\right.\]
  416.       # (Of course we only define it for values 'x' which occur as possible
  417.       # representative orders.)
  418.  
  419.       # Then we must have 'CompositionMaps( orders, factortable.powermap[p] )
  420.       # = CompositionMaps( rpow[p], orders )', so 'TestConsistencyMaps'
  421.       # is called.
  422.  
  423.       values:= [];
  424.       for i in orders do
  425.         if IsInt( i ) then
  426.           AddSet( values, i );
  427.         else
  428.           values:= Union( values, i );
  429.         fi;
  430.       od;
  431.       rpow:= [];
  432.       for i in Set( Factors( factortable.order ) ) do
  433.         rpow[i]:= [];
  434.         for x in values do
  435.           if x mod i = 0 then
  436.             rpow[i][x]:= x / i;
  437.           else
  438.             rpow[i][x]:= x;
  439.           fi;
  440.         od;
  441.       od;
  442.       TestConsistencyMaps( factortable.powermap, orders, rpow );
  443.       if InfoCharTable2 <> Ignore then
  444.         dubious:= [];
  445.         for i in [ 1 .. Length( orders ) ] do
  446.           if IsList( orders[i] ) then Add( dubious, i ); fi;
  447.         od;
  448.         if dubious <> [] then
  449.           Print( "#I CharTableFactorGroup: The representative order is",
  450.                  " dubious\n",
  451.                  "#I    for classes", dubious, "\n" );
  452.         fi;
  453.       fi;
  454.  
  455.       factortable.orders:= orders;
  456.     fi;
  457.  
  458.     # store the factor fusion on <tbl>;
  459.     StoreFusion( tbl, factortable,
  460.                  rec( map:= factorfusion, type:= "factor" ) );
  461.     
  462.     InitClassesCharTable( factortable );
  463.     factortable.operations:= CharTableOps;
  464.     
  465.     return factortable;
  466.     end;
  467.  
  468.  
  469. #############################################################################
  470. ##
  471. #F  CharTableNormalSubgroup( <tbl>, <classes\_of\_normal\_subgroup> )
  472. ##
  473. ##  returns the restriction of the character table <tbl> to the classes in
  474. ##  the list <classes\_of\_normal\_subgroup>.
  475. ##  This table is an approximation of the character table of this normal
  476. ##  subgroup. It has fields 'order', 'name', 'centralizers', 'orders',
  477. ##  'classes', 'powermap', 'irreducibles' (contains those restrictions of
  478. ##  irreducibles of <tbl> which are irreducible), and 'fusions' (contains
  479. ##  the fusion in <tbl>).
  480. ##
  481. ##  In most cases, some classes of the normal subgroup must be split, see
  482. ##  "CharTableSplitClasses".
  483. ##
  484. CharTableNormalSubgroup := function( tbl, classes_of_normal_subgroup )
  485.     local p, result, err, inverse, chi, char;
  486.  
  487.     result:= rec( name:= ConcatenationString( "Rest(", tbl.name, ",",
  488.                          String( classes_of_normal_subgroup ), ")" ) );
  489.     result.order:= Sum( Sublist( tbl.classes, classes_of_normal_subgroup ) );
  490.     result.centralizers:= List( classes_of_normal_subgroup,
  491.                                 x -> result.order / tbl.classes[x] );
  492.     result.orders:= Sublist( tbl.orders, classes_of_normal_subgroup );
  493.     err:= Filtered( [ 1 .. Length( result.centralizers ) ],
  494.                     x->not IsInt(result.centralizers[x]/result.orders[x]) );
  495.     if err <> [] then
  496.       Print( "#I CharTableNormalSubgroup:",
  497.              " classes in " , err, " necessarily split\n" );
  498.     fi;
  499.     result.powermap:= [];
  500.     inverse:= InverseMap( classes_of_normal_subgroup );
  501.     if IsBound( tbl.powermap ) then
  502.       for p in [ 1 .. Length( tbl.powermap ) ] do
  503.         if IsBound( tbl.powermap[p] ) then
  504.           result.powermap[p]:=
  505.               CompositionMaps( inverse, CompositionMaps( tbl.powermap[p],
  506.                                              classes_of_normal_subgroup ) );
  507.         fi;
  508.       od;
  509.     fi;
  510.  
  511.     result.classes:= Sublist( tbl.classes, classes_of_normal_subgroup );
  512.     result.operations:= CharTableOps;
  513.  
  514.     if tbl.order mod result.order <> 0 then
  515.       Print( "#E CharTableNormalSubgroup:",
  516.              " list of classes is not a normal subgroup\n" );
  517.     else
  518.       result.irreducibles:= [];
  519.       for chi in tbl.irreducibles do
  520.         char:= Sublist( chi, classes_of_normal_subgroup );
  521.         if ScalarProduct( result, char, char ) = 1 and
  522.            not char in result.irreducibles then
  523.           Add( result.irreducibles, char );
  524.         fi;
  525.       od;
  526.       p:= tbl.order / result.order;
  527.       if IsPrimeInt( p ) then
  528.         InfoCharTable2( "#I CharTableNormalSubgroup: The table must have ",
  529.                         p * Length( tbl.centralizers ) -
  530.                         ( p^2 - 1 ) * Length( result.irreducibles ),
  531.                         " classes\n#I   (now ", Length(result.centralizers),
  532.                         ", after nec. splitting ",
  533.                         Length(result.centralizers) + (p-1) * Length( err ),
  534.                         ")\n" );
  535.       fi;
  536.     fi;
  537.     StoreFusion( result, tbl, Copy( classes_of_normal_subgroup ) );
  538.  
  539.     return result;
  540.     end;
  541.  
  542.  
  543. #############################################################################
  544. ##
  545. #F  CharTableSplitClasses( <tbl>, <fusionmap> )
  546. #F  CharTableSplitClasses( <tbl>, <fusionmap>, <exponent> )
  547. ##
  548. ##  returns a table where the classes of <tbl> are split according to
  549. ##  <fusionmap>; the first version is for splitting in normal subgroups,
  550. ##  the second for splitting in downward extensions.
  551. ##
  552. ##  concerned fields of the table record:
  553. ##
  554. ##  'name':         '<tbl>.name' concatenated with the string of <fusionmap>;
  555. ##  'order':        unchanged
  556. ##  'centralizers': will be adjusted in the first case,
  557. ##                  will only be split up in the second case;
  558. ##  'classes':      will be adjusted in the first case, will be divided by
  559. ##                  the number of images in the second case;
  560. ##  'powermap':     all contained maps are adjusted
  561. ##                  (and will probably be parametrized afterwards);
  562. ##  'orders':       will be split in the first case,
  563. ##                  in the second case we have\:
  564. ##          A first approximation is given by the fact that
  565. ##          'tbl.orders[ <fusionmap>[i] ]' divides 'orders[i]' and 'orders[i]'
  566. ##          divides '<exponent> \* 'orders[ <fusionmap>[i] ]', where
  567. ##          <exponent> is regarded as the exponent of the normal subgroup
  568. ##          extending <tbl>.
  569. ##  'irreducibles': all irreducibles are indirected by <fusionmap>;
  570. ##  'fusions':      <fusionmap> is stored as factor fusion, and
  571. ##                  <tbl>.fusionsource is adjusted
  572. ##
  573. CharTableSplitClasses := function( arg )
  574.     local i, j, p, tbl, fusionmap, exponent, split, divs, inverse, len, class;
  575.  
  576.     if not ( Length( arg ) in [ 2, 3 ] and IsCharTable( arg[1] ) and
  577.              IsList( arg[2] ) )
  578.        or ( Length( arg ) = 2 and KernelChar( arg[2] ) <> [ 1 ] )
  579.        or ( Length( arg ) = 3 and not IsInt( arg[3] )
  580.                               and KernelChar( arg[2] ) = [ 1 ] ) then
  581.       Error( "usage: CharTableSplitClasses( <tbl>, <fusionmap> )\n",
  582.              " ( for splitting in normal subgroup )\n",
  583.              " resp. CharTableSplitClasses(<tbl>,<fusionmap>,<exp>)\n",
  584.              " ( for splitting in downward extension with normal subgroup\n",
  585.              " of exponent <exp> )" );
  586.     fi;
  587.     tbl:= arg[1];
  588.     fusionmap:= arg[2];
  589.     inverse:= InverseMap( fusionmap );
  590.     split:= rec( name:= ConcatenationString( "Split(", tbl.name,
  591.                                              ",", String( fusionmap ), ")" ),
  592.                  order:= tbl.order );
  593.     if Length( arg ) = 2 then   # splitting in normal subgroup
  594.       split.centralizers:= [];
  595.       split.classes:= [];
  596.       for i in [  1.. Length( inverse ) ] do
  597.         if IsInt( inverse[i] ) then
  598.           split.centralizers[ inverse[i] ]:= tbl.centralizers[i];
  599.           split.classes[ inverse[i] ]:= tbl.classes[i];
  600.         else
  601.           len:= Length( inverse[i] );
  602.           for j in inverse[i] do
  603.             split.centralizers[j]:= tbl.centralizers[i] * len;
  604.             split.classes[j]:= tbl.classes[i] / len;
  605.           od;
  606.         fi;
  607.       od;
  608.       split.orders:= Indirected( tbl.orders, fusionmap );
  609.     else                        # downward extension
  610.       exponent:= arg[3];
  611.       split.centralizers:= Indirected( tbl.centralizers, fusionmap );
  612.       split.classes:= [];
  613.       for i in [ 1 .. Length( inverse ) ] do
  614.         if IsInt( inverse[i] ) then
  615.           split.classes[ inverse[i] ]:= tbl.classes[i];
  616.         else
  617.           for j in inverse[i] do
  618.             split.classes[j]:= tbl.classes[i] / Length( inverse[i] );
  619.           od;
  620.         fi;
  621.       od;
  622.       divs:= DivisorsInt( exponent );
  623.       split.orders:= Indirected( List( tbl.orders, x -> x*divs ), fusionmap );
  624.       split.orders[1]:= 1;
  625.       for i in [ 2 .. Length( fusionmap ) ] do
  626.         if fusionmap[i] = 1 then                # delete order 1
  627.           split.orders[i]:= Difference( split.orders[i], [ 1 ] );
  628.         fi;
  629.         if Length( split.orders[i] ) = 1 then
  630.           split.orders[i]:= split.orders[i][1];
  631.         fi;
  632.       od;
  633.     fi;
  634.     if IsBound( tbl.powermap ) then
  635.       split.powermap:= [];
  636.       for i in [ 1 .. Length( tbl.powermap ) ] do
  637.         if IsBound( tbl.powermap[i] ) then
  638.           split.powermap[i]:=
  639.                CompositionMaps( inverse,
  640.                                 CompositionMaps(tbl.powermap[i],fusionmap) );
  641.           split.powermap[i][1]:= 1;
  642.         fi;
  643.       od;
  644.       if Length( arg ) = 3 then
  645.  
  646.         # try to improve the powermaps inside the preimage of 1A:
  647.         # if <exponent> is a prime power p^n, the nontrivial preimages
  648.         # cannot map to 1A in powermaps prime to p; they cannot power to
  649.         # themselves in the p-th powermap; they must power to 1A if
  650.         # '<exponent> = p'.
  651.         divs:= Set( Factors( exponent ) );
  652.         if Length( divs ) = 1 and IsList( inverse[1] ) then
  653.           i:= divs[1];
  654.           for class in Difference( inverse[1], [ 1 ] ) do
  655.             for p in [ 1 .. Length( split.powermap ) ] do
  656.               if IsBound( split.powermap[p] ) then
  657.                 if GcdInt( i, p ) = 1 then
  658.                   if IsList( split.powermap[p][ class ] ) then
  659.                     split.powermap[p][ class ]:=
  660.                              Difference( split.powermap[p][ class ], [1] );
  661.                     if Length( split.powermap[p][ class ] ) = 1 then
  662.                       split.powermap[p][ class ]:=
  663.                               split.powermap[p][ class ][1];
  664.                     fi;
  665.                   fi;
  666.                 elif p = i then
  667.                   if i = exponent then
  668.                     split.powermap[p][ class ]:= 1;
  669.                   else
  670.                     if IsList( split.powermap[p][ class ] ) then
  671.                       split.powermap[p][ class ]:=
  672.                              Difference( split.powermap[p][class], [class] );
  673.                       if Length( split.powermap[p][ class ] ) = 1 then
  674.                         split.powermap[p][ class ]:=
  675.                                 split.powermap[p][ class ][1];
  676.                       fi;
  677.                     fi;
  678.                   fi;
  679.                 fi;
  680.               fi;
  681.             od;
  682.           od;
  683.         fi;
  684.       fi;
  685.     fi;
  686.     if IsBound( tbl.irreducibles ) then
  687.       split.irreducibles:= Restricted( tbl.irreducibles, fusionmap );
  688.     fi;
  689.     StoreFusion( split, tbl, fusionmap ); 
  690.     
  691.     split.operations:= CharTableOps;
  692.     
  693.     return split;
  694.     end;
  695.  
  696.  
  697. #############################################################################
  698. ##
  699. #F  CharTableCollapsedClasses( <tbl>, <fusionmap> )
  700. ##
  701. ##  returns a table where the classes of <tbl> are collapsed according to
  702. ##  <fusionmap>.
  703. ##
  704. ##  concerned fields of the table record:
  705. ##
  706. ##  'name':         '<tbl>.name' concatenated with the string of <fusionmap>;
  707. ##  'order':        unchanged
  708. ##  'classes':      sum over classlengths of preimages
  709. ##  'centralizers': adjusted with respect to 'order' and 'classes'
  710. ##  'powermap':     all contained maps are adjusted
  711. ##  'orders':         are collapsed
  712. ##  'irreducibles': indirections by the inverse of <fusionmap> of all
  713. ##                  irreducibles that collapse uniquely;
  714. ##  'fusions':      <fusionmap> is stored as fusion on <tbl>, and
  715. ##                  the fusionsource of the new table is adjusted
  716. ##
  717. ##  If some entries of 'orders' or 'powermap' become
  718. ##  parametrized because the values differ for preimages, a warning is
  719. ##  printed.
  720. ##
  721. CharTableCollapsedClasses := function( tbl, fusionmap )
  722.     local i, p, collaps, inverse, classes;
  723.     collaps:= rec( name:= ConcatenationString( "Collapsed(", tbl.name, ",",
  724.                                                String( fusionmap ), ")" ),
  725.                    order:= tbl.order );
  726.     inverse:= InverseMap( fusionmap );
  727.     classes:= List( [ 1 .. Maximum( fusionmap ) ], x -> 0 );
  728.     for i in [ 1 .. Length( fusionmap ) ] do
  729.       classes[ fusionmap[i] ]:= classes[ fusionmap[i] ] + tbl.classes[i];
  730.     od;
  731.     collaps.centralizers:= List( classes, x -> collaps.order / x );
  732.     collaps.orders:= CompositionMaps( tbl.orders, inverse );
  733.     collaps.powermap:= [];
  734.     if IsBound( tbl.powermap ) then
  735.       for i in [ 1 .. Length( tbl.powermap ) ] do
  736.         if IsBound( tbl.powermap[i] ) then
  737.           collaps.powermap[i]:=
  738.                 CompositionMaps( fusionmap,
  739.                                  CompositionMaps(tbl.powermap[i],inverse) );
  740.         fi;
  741.       od;
  742.     fi;
  743.     collaps.fusionsource:= [];
  744.     StoreFusion( tbl, collaps, fusionmap );
  745.     collaps.irreducibles:= Filtered( List( tbl.irreducibles,
  746.                                     x -> CompositionMaps( x, inverse ) ),
  747.                                     y -> ForAll( y, IsCyc ) );
  748.     collaps.classes:= classes;
  749.     for i in [ 1 .. Length( classes ) ] do
  750.       if IsList( inverse[i] ) then
  751.         if IsList( collaps.orders[i] )
  752.            and ForAll( inverse[i], x -> IsInt( tbl.orders[x] ) ) then
  753.           Print( "#E CharTableCollapsedClasses: orders in ", inverse[i],
  754.                  " are different\n" );
  755.         fi;
  756.         for p in [ 1 .. Length( collaps.powermap ) ] do
  757.           if IsBound(collaps.powermap[p]) and IsList(collaps.powermap[p][i])
  758.              and ForAll( inverse[i], x -> IsInt( tbl.powermap[p][x] ) ) then
  759.             Print( "#E CharTableCollapsedClasses: classes in ",
  760.                    collaps.powermap[p][i], " must collapse because of\n",
  761.                    "#E    ", Ordinal( p ), " powermap\n" );
  762.           fi;
  763.         od;
  764.       fi;
  765.     od;
  766.     collaps.operations:= CharTableOps;
  767.     
  768.     return collaps;
  769.     end;
  770.  
  771.  
  772. #############################################################################
  773. ##
  774. #F  CharTableIsoclinic( <tbl> )
  775. #F  CharTableIsoclinic( <tbl>, <classes_of_normal_subgroup> )
  776. ##
  777. ##  for table of groups $2.G.2$, the character table of the isoclinic group
  778. ##  (see ATLAS, Chapter 6, Section 7)
  779. ##
  780. CharTableIsoclinic := function( arg )
  781.     local i, j, x, y, chi, class, map, tbl, linear, isoclinic, center, outer,
  782.           normal_subgroup, images, factorfusion;
  783.  
  784.     if not ( Length( arg ) in [ 1, 2 ] and IsCharTable( arg[1] ) )
  785.        or ( Length( arg ) = 2 and not IsList( arg[2] ) ) then
  786.       Error( "usage: CharTableIsoclinic( tbl ) resp.\n",
  787.              "       CharTableIsoclinic( tbl, classes_of_normal_subgroup )");
  788.     fi;
  789.     tbl:= arg[1];
  790.     if Length( arg ) = 1 then
  791.       linear:= Filtered( tbl.irreducibles, x -> x[1] = 1 );
  792.       for chi in linear do
  793.         if ForAll( chi, x -> x = 1 )
  794.            or Sum(Sublist(tbl.classes,KernelChar(chi))) <> tbl.order /2 then
  795.           linear:= Difference( linear, [ chi ] );
  796.         fi;
  797.       od;
  798.       if Length( linear ) > 1 then
  799.         Error( "normal subgroup of index 2 not uniquely determined,\n",
  800.                "use CharTableIsoclinic( tbl, classes_of_normal_subgroup )" );
  801.       fi;
  802.       normal_subgroup:= KernelChar( linear[1] );
  803.     else
  804.       if Sum( Sublist( tbl.classes, arg[2] ) ) <> tbl.order / 2 then
  805.         Error( "normal subgroup must have index 2" );
  806.       fi;
  807.       normal_subgroup:= arg[2];
  808.     fi;
  809.     center:= Filtered( [ 1 .. Length( tbl.classes ) ],
  810.                        x -> tbl.centralizers[1] = tbl.centralizers[x] );
  811.     if Length( center ) <> 2 then
  812.       Error( "There must be exactly two central elements" );
  813.     fi;
  814.     isoclinic:= rec( name:= ConcatenationString( "Isoclinic(",tbl.name,")" ),
  815.                      order:= tbl.order,
  816.                      centralizers:= Copy( tbl.centralizers ),
  817.                      orders:= Copy( tbl.orders ),
  818.                      fusions:= [],
  819.                      fusionsource:= [],
  820.                      powermap:= [],
  821.                      irreducibles:= Copy( tbl.irreducibles ) );
  822.     InitClassesCharTable( isoclinic );
  823.     for j in [ 1 .. Length( tbl.powermap ) ] do
  824.       if IsPrimeInt( j ) and IsBound( tbl.powermap[j] ) then
  825.         isoclinic.powermap[j]:= Copy( tbl.powermap[j] );
  826.       fi;
  827.     od;
  828.     i:= E(4);
  829.     outer:= Difference( [ 1 .. Length( tbl.classes ) ], normal_subgroup );
  830.     for chi in Filtered( isoclinic.irreducibles,     # faithful characters
  831.                          x -> ForAny( center, y -> x[y] <> x[1] ) ) do
  832.       for class in outer do
  833.         chi[ class ]:= i * chi[ class ];
  834.       od;
  835.     od;
  836.     CharTableFactorGroup( isoclinic, center );   # very strange ...
  837.     factorfusion:= isoclinic.fusions[1].map;
  838.     isoclinic.fusions:= [];
  839.     for j in [ 1 .. Length( isoclinic.powermap ) ] do
  840.       if IsBound( isoclinic.powermap[j] ) then
  841.         map:= isoclinic.powermap[j];
  842.         if j = 2 then
  843.  
  844.           # The squares lie in 'normal_subgroup'; for $g^2 = h$,
  845.           # we have $(gi)^2 = hz$, so we must take the other
  846.           # preimage under the factorfusion, if exists.
  847.           for class in outer do
  848.             images:= Filtered( Difference( normal_subgroup, [ map[class] ] ),
  849.                               x -> factorfusion[x]
  850.                                    = factorfusion[ map[ class ] ] );
  851.             if Length( images ) = 1 then
  852.               map[ class ]:= images[1];
  853.               isoclinic.orders[ class ]:= 2 * isoclinic.orders[ images[1] ];
  854.             fi;
  855.           od;
  856.         elif j mod 4 = 3 then
  857.     
  858.           # For $g^p = h$, we have $(gi)^p = hi^p = hiz$, so again
  859.           # we must choose the other preimage under the
  860.           # factorfusion, if exists; the 'p'-th powers lie outside
  861.           # 'normal_subgroup' in this case.
  862.           for class in outer do
  863.             images:= Filtered( Difference( outer, [ map[ class ] ] ),
  864.                               x -> factorfusion[x]
  865.                                    = factorfusion[ map[ class ] ] );
  866.             if Length( images ) = 1 then
  867.               map[ class ]:= images[1];
  868.             fi;
  869.           od;
  870.         fi;        # For j mod 4 = 1, the map remains unchanged, since
  871.                    # $g^p = h$ and $(gi)^p = hi^p = hi$ then.
  872.       fi;
  873.     od;
  874.     isoclinic.operations:= CharTableOps;
  875.     
  876.     return isoclinic;
  877.     end;
  878.  
  879.  
  880. #############################################################################
  881. ##
  882. #F  CharTableQuaternionic( <4n> )
  883. ##
  884. ##  table of the quaternionic group of order <4n>
  885. ##
  886. CharTableQuaternionic := function( four_n )
  887.     local quaternionic;
  888.  
  889.     if four_n mod 4 <> 0 then
  890.       Error( "argument must be a multiple of 4" );
  891.     fi;
  892.     if four_n = 4 then
  893.       quaternionic:= CharTable( "Cyclic", 4 );
  894.     else
  895.       quaternionic:= CharTableIsoclinic( CharTable( "Dihedral", four_n ),
  896.                                          [1..four_n/4+1] );
  897.     fi;
  898.     quaternionic.name:= ConcatenationString( "Q", String( four_n ) );
  899.     return quaternionic;
  900.     end;
  901.  
  902.  
  903. #############################################################################
  904. ##
  905. #F  PrimeBase( q ) . . . . . . . . . . . . . . . Compute the Characteristic.
  906. ##
  907. ##  If q is a prime power, PrimeBase computes the prime of which it is a Power.
  908. ##  For the sake of speed, the results are stored in GEN_Q_P.
  909. ##
  910. GEN_Q_P := [];
  911.  
  912. PrimeBase := function( q )
  913.     if Length( GEN_Q_P ) < q or not IsBound( GEN_Q_P[q] ) then
  914.       GEN_Q_P[q] := FactorsInt( q )[1];
  915.     fi;
  916.     return( GEN_Q_P[q] );
  917.     end;
  918.  
  919. #############################################################################
  920. ##
  921. #F  CharTableSpecialized( <generic character table>, <q> )  . . Specialise q.
  922. ##
  923. ##  This function does the actual specialisation.
  924. ##
  925.  
  926. CharTableSpecialized := function( gtab, q )
  927.  
  928.     local complete, irred, gen, taf, i, j, k, l, lencl, lench, parch, parcl,
  929.           chr, genclass, classparam, genchar, charparam, class, parm;
  930.       
  931.     taf := rec();
  932.     complete := true;
  933.     
  934.     #  A generic character table must contain at least functions to compute
  935.     #  the parametrisation of classes and characters.
  936.     
  937.     if not IsBound( gtab.classparam ) or not IsBound( gtab.charparam ) then
  938.       Error("This is not a generic character table.");
  939.     fi;
  940.     
  941.     #  Check if the argument is valid.
  942.     
  943.     if IsBound( gtab.domain ) and gtab.domain( q ) = false then
  944.       Error( q, " is not a valid paramater for this generic table" );
  945.     fi;
  946.     
  947.     #  If the generic table has a field 'wholetable' (a function which takes
  948.     #  the generic table and 'q' as parameter), use this function to
  949.     #  construct the whole table.
  950.     
  951.     if IsBound( gtab.wholetable ) then
  952.       taf:= gtab.wholetable( gtab, q );
  953.     else
  954.     
  955.       #  Get the parametrisation of classes and characters. Genclass stores
  956.       #  for each class of the special character table the number of the
  957.       #  class of the generic table gtab it stems from. Classparm stores the
  958.       #  parameter of the special class. Genchar and Charparm do the same for
  959.       #  characters.
  960.     
  961.       genclass := []; classparam := [];
  962.     
  963.       for i in [1..Length(gtab.classparam)] do
  964.         parm := gtab.classparam[i](q);
  965.         Append( classparam, parm );
  966.         Append( genclass, List( parm, j->i ) );
  967.       od;
  968.     
  969.       genchar := []; charparam := [];
  970.     
  971.       for i in [1..Length(gtab.charparam)] do
  972.         parm := gtab.charparam[i](q);
  973.         Append( charparam, parm );
  974.         Append( genchar, List( parm, j->i ) );
  975.       od;
  976.     
  977.       #  Compute the name of the table.
  978.     
  979.       if IsBound( gtab.specializedname ) then
  980.         taf.name:= gtab.specializedname( q );
  981.       else
  982.         complete := false;
  983.       fi;
  984.     
  985.       #  Compute the group order.
  986.     
  987.       if IsBound( gtab.order ) then
  988.         taf.order := gtab.order(q);
  989.       else complete := false;
  990.       fi;
  991.     
  992.       #  Compute centralizer and representative orders.
  993.     
  994.       if IsBound(gtab.centralizers) then 
  995.         taf.centralizers := List( [1..Length(classparam)], ( j ->
  996.           gtab.centralizers[ genclass[j] ]( q, classparam[j] )));
  997.       else
  998.         complete := false;
  999.       fi;
  1000.     
  1001.       if IsBound(gtab.orders) then 
  1002.         taf.orders := List( [1..Length(classparam)], ( j ->
  1003.           gtab.orders[ genclass[j] ]( q, classparam[j] )));
  1004.       else
  1005.         complete := false;
  1006.       fi;
  1007.     
  1008.       #  Compute the powermap.
  1009.     
  1010.       taf.powermap := [];
  1011.       if IsBound( gtab.powermap ) and IsBound( taf.order ) then
  1012.         for i in Reversed(Set( Factors( taf.order ))) do
  1013.           taf.powermap[i] := [];
  1014.           for class in Reversed([1..Length( classparam )]) do
  1015.             parm := gtab.powermap[genclass[class]](q, classparam[class],i);
  1016.             k := 1;
  1017.             while genclass[k] <> parm[1] or classparam[k] <> parm[2] do 
  1018.               k := k+1;
  1019.             od;
  1020.             taf.powermap[i][class] := k;
  1021.           od;
  1022.         od;
  1023.       fi;
  1024.           
  1025.       #  Perform some initialisations, if the necessary data are present.
  1026.     
  1027.       if IsBound(gtab.classtext) then 
  1028.         taf.classtext := List( [1..Length(classparam)], ( j ->
  1029.           gtab.classtext[ genclass[j] ]( q, classparam[j] )));
  1030.       fi;
  1031.     
  1032.       #  Compute the character values.
  1033.     
  1034.       if IsBound( gtab.matrix ) then
  1035.         taf.irreducibles := gtab.matrix( q );
  1036.       elif IsBound(gtab.irreducibles) then
  1037.         taf.irreducibles :=
  1038.             List( [1..Length(charparam)],
  1039.                   i -> List( [1..Length(classparam)],
  1040.                              j -> gtab.irreducibles[genchar[i]][genclass[j]]
  1041.                                   ( q, charparam[i], classparam[j] ) ) );
  1042.       fi;
  1043.     
  1044.       taf.classparam := List( [ 1 .. Length( classparam ) ],
  1045.                               i -> [ genclass[i], classparam[i] ] );
  1046.       taf.irredinfo:= List( [ 1 .. Length( charparam ) ],
  1047.                             i->rec(charparam:= [genchar[i],charparam[i]]) );
  1048.     
  1049.       if IsBound( gtab.text ) 
  1050.         then taf.text:=ConcatenationString("computed using ",gtab.text);
  1051.       fi;
  1052.     fi;
  1053.     
  1054.     if complete then
  1055.       InitClassesCharTable( taf );
  1056.       taf.operations:= CharTableOps;
  1057.     fi;
  1058.     return taf;
  1059.     end;
  1060.  
  1061.