Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/GAP/lib/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 18.9.2025 mit Größe 165 kB image not shown  

Quelle  ctblfuns.gi   Sprache: unbekannt

 
#############################################################################
##
##  This file is part of GAP, a system for computational discrete algebra.
##  This file's authors include Thomas Breuer.
##
##  Copyright of GAP belongs to its developers, whose names are too numerous
##  to list here. Please refer to the COPYRIGHT file for details.
##
##  SPDX-License-Identifier: GPL-2.0-or-later
##
##  This file contains generic methods for class functions.
##
##  2. Basic Operations for Class Functions
##  3. Comparison of Class Functions
##  4. Arithmetic Operations for Class Functions
##  5. Printing Class Functions
##  6. Creating Class Functions from Values Lists
##  7. Creating Class Functions using Groups
##  8. Operations for Class Functions
##  9. Restricted and Induced Class Functions
##  10. Reducing Virtual Characters
##  11. Symmetrizations of Class Functions
##  12. Operations for Brauer Characters
##  13. Domains Generated by Class Functions
##  14. Auxiliary operations
##


#############################################################################
##
#F  CharacterString( <char>, <str> )  . . . . .  character information string
##
InstallGlobalFunction( CharacterString, function( char, str )
    str:= Concatenation( str, " of degree ", String( char[1] ) );
    ConvertToStringRep( str );
    return str;
end );


#############################################################################
##
##  2. Basic Operations for Class Functions
##


#############################################################################
##
#M  ValuesOfClassFunction( <list> )
##
##  In order to treat lists as class functions where this makes sense,
##  we define that `ValuesOfClassFunction' returns the list <list> itself.
##
InstallOtherMethod( ValuesOfClassFunction,
    "for a dense list",
    [ IsDenseList ],
    function( list )
    if IsClassFunction( list ) and not HasValuesOfClassFunction( list ) then
      Error( "class function <list> must store its values list" );
    else
      return list;
    fi;
    end );


#############################################################################
##
#M  \[\]( <psi>, <i> )
#M  Length( <psi> )
#M  IsBound\[\]( <psi>, <i> )
#M  Position( <psi>, <obj>, 0 )
##
##  Class functions shall behave as immutable lists,
##  we install methods for `\[\]', `Length', `IsBound\[\]', `Position',
##  and `ShallowCopy'.
##
InstallMethod( \[\],
    "for class function and positive integer",
    [ IsClassFunction, IsPosInt ],
    function( chi, i )
    return ValuesOfClassFunction( chi )[i];
    end );

InstallMethod( Length,
    "for class function",
    [ IsClassFunction ],
    chi -> Length( ValuesOfClassFunction( chi ) ) );

InstallMethod( IsBound\[\],
    "for class function and positive integer",
    [ IsClassFunction, IsPosInt ],
    function( chi, i )
    return IsBound( ValuesOfClassFunction( chi )[i] );
    end );

InstallMethod( Position,
    "for class function, cyclotomic, and nonnegative integer",
    [ IsClassFunction, IsCyc, IsInt ],
    function( chi, obj, pos )
    return Position( ValuesOfClassFunction( chi ), obj, pos );
    end );

InstallMethod( ShallowCopy,
    "for class function",
    [ IsClassFunction ],
    chi -> ShallowCopy( ValuesOfClassFunction( chi ) ) );


#############################################################################
##
#M  UnderlyingGroup( <chi> )
##
InstallOtherMethod( UnderlyingGroup,
    "for a class function",
    [ IsClassFunction ],
    chi -> UnderlyingGroup( UnderlyingCharacterTable( chi ) ) );


#############################################################################
##
##  3. Comparison of Class Functions
##


#############################################################################
##
#M  \=( <chi>, <psi> )  . . . . . . . . . . . . . equality of class functions
##
InstallMethod( \=,
    "for two class functions",
    [ IsClassFunction, IsClassFunction ],
    function( chi, psi )
    return ValuesOfClassFunction( chi ) = ValuesOfClassFunction( psi );
    end );

InstallMethod( \=,
    "for a class function and a list",
    [ IsClassFunction, IsList ],
    function( chi, list )
    if IsClassFunction( list ) then
      return ValuesOfClassFunction( chi ) = ValuesOfClassFunction( list );
    else
      return ValuesOfClassFunction( chi ) = list;
    fi;
    end );

InstallMethod( \=,
    "for a list and a class function",
    [ IsList, IsClassFunction ],
    function( list, chi )
    if IsClassFunction( list ) then
      return ValuesOfClassFunction( list ) = ValuesOfClassFunction( chi );
    else
      return list = ValuesOfClassFunction( chi );
    fi;
    end );


#############################################################################
##
#M  \<( <chi>, <psi> )  . . . . . . . . . . . . comparison of class functions
##
InstallMethod( \<,
    "for two class functions",
    [ IsClassFunction, IsClassFunction ],
    function( chi, psi )
    return ValuesOfClassFunction( chi ) < ValuesOfClassFunction( psi );
    end );

InstallMethod( \<,
    "for a class function and a list",
    [ IsClassFunction, IsList ],
    function( chi, list )
    if IsClassFunction( list ) then
      return ValuesOfClassFunction( chi ) < ValuesOfClassFunction( list );
    else
      return ValuesOfClassFunction( chi ) < list;
    fi;
    end );

InstallMethod( \<,
    "for a list and a class function",
    [ IsClassFunction, IsList ],
    function( list, chi )
    if IsClassFunction( list ) then
      return ValuesOfClassFunction( list ) < ValuesOfClassFunction( chi );
    else
      return list < ValuesOfClassFunction( chi );
    fi;
    end );


#############################################################################
##
##  4. Arithmetic Operations for Class Functions
##


#############################################################################
##
#M  \+( <chi>, <obj> )  . . . . . . . . . .  sum of class function and object
#M  \+( <obj>, <chi> )  . . . . . . . . . .  sum of object and class function
#M  \+( <chi>, <psi> )  . . . . . . . . . . . . . . sum of virtual characters
#M  \+( <chi>, <psi> )  . . . . . . . . . . . . . . . . . . sum of characters
##
##  The sum of two class functions (virtual characters, characters) of the
##  same character table is again a class function (virtual character,
##  character) of this table.
##  In all other cases, the addition is delegated to the list of values of
##  the class function(s).
##
InstallOtherMethod( \+,
    "for class function, and object",
    [ IsClassFunction, IsObject ],
    function( chi, obj )
    local tbl, sum;
    tbl:= UnderlyingCharacterTable( chi );
    if IsClassFunction( obj ) and
       IsIdenticalObj( tbl, UnderlyingCharacterTable( obj ) ) then
      sum:= ClassFunction( tbl,
              ValuesOfClassFunction( chi ) + ValuesOfClassFunction( obj ) );
    else
      sum:= ValuesOfClassFunction( chi ) + obj;
    fi;
    return sum;
    end );

InstallOtherMethod( \+,
    "for object, and class function",
    [ IsObject, IsClassFunction ],
    function( obj, chi )
    local tbl, sum;
    tbl:= UnderlyingCharacterTable( chi );
    if IsClassFunction( obj ) and
       IsIdenticalObj( tbl, UnderlyingCharacterTable( obj ) ) then
      sum:= ClassFunction( tbl,
              ValuesOfClassFunction( obj ) + ValuesOfClassFunction( chi ) );
    else
      sum:= obj + ValuesOfClassFunction( chi );
    fi;
    return sum;
    end );

InstallMethod( \+,
    "for two virtual characters",
    IsIdenticalObj,
    [ IsClassFunction and IsVirtualCharacter,
      IsClassFunction and IsVirtualCharacter ],
    function( chi, psi )
    local tbl, sum;
    tbl:= UnderlyingCharacterTable( chi );
    sum:= ValuesOfClassFunction( chi ) + ValuesOfClassFunction( psi );
    if IsIdenticalObj( tbl, UnderlyingCharacterTable( psi ) ) then
      sum:= VirtualCharacter( tbl, sum );
    fi;
    return sum;
    end );

InstallMethod( \+,
    "for two characters",
    IsIdenticalObj,
    [ IsClassFunction and IsCharacter, IsClassFunction and IsCharacter ],
    function( chi, psi )
    local tbl, sum;
    tbl:= UnderlyingCharacterTable( chi );
    sum:= ValuesOfClassFunction( chi ) + ValuesOfClassFunction( psi );
    if IsIdenticalObj( tbl, UnderlyingCharacterTable( psi ) ) then
      sum:= Character( tbl, sum );
    fi;
    return sum;
    end );


#############################################################################
##
#M  AdditiveInverseOp( <psi> )  . . . . . . . . . . . .  for a class function
##
##  The additive inverse of a virtual character is again a virtual character,
##  but the additive inverse of a character is not a character,
##  so  we cannot use `ClassFunctionSameType'.
##
InstallMethod( AdditiveInverseOp,
    "for a class function",
    [ IsClassFunction ],
    psi -> ClassFunction( UnderlyingCharacterTable( psi ),
               AdditiveInverse( ValuesOfClassFunction( psi ) ) ) );


InstallMethod( AdditiveInverseOp,
    "for a virtual character",
    [ IsClassFunction and IsVirtualCharacter ],
    psi -> VirtualCharacter( UnderlyingCharacterTable( psi ),
               AdditiveInverse( ValuesOfClassFunction( psi ) ) ) );


#############################################################################
##
#M  ZeroOp( <psi> ) . . . . . . . . . . . . . . . . . .  for a class function
##
InstallMethod( ZeroOp,
    "for a class function",
    [ IsClassFunction ],
    psi -> VirtualCharacter( UnderlyingCharacterTable( psi ),
               Zero( ValuesOfClassFunction( psi ) ) ) );


#############################################################################
##
#M  \*( <cyc>, <psi> )  . . . . . . . . . scalar multiple of a class function
#M  \*( <psi>, <cyc> )  . . . . . . . . . scalar multiple of a class function
##
##  We define a multiplication only for two class functions (being the tensor
##  product), for scalar multiplication with cyclotomics,
##  and for default list times class function (where the class function acts
##  as a scalar).
##  Note that more is not needed, since class functions are not in
##  `IsMultiplicativeGeneralizedRowVector'.
##
InstallMethod( \*,
    "for cyclotomic, and class function",
    [ IsCyc, IsClassFunction ],
    function( cyc, chi )
    return ClassFunction( UnderlyingCharacterTable( chi ),
               cyc * ValuesOfClassFunction( chi ) );
    end );

InstallMethod( \*,
    "for integer, and virtual character",
    [ IsInt, IsVirtualCharacter ],
    function( cyc, chi )
    return VirtualCharacter( UnderlyingCharacterTable( chi ),
               cyc * ValuesOfClassFunction( chi ) );
    end );

InstallMethod( \*,
    "for positive integer, and character",
    [ IsPosInt, IsCharacter ],
    function( cyc, chi )
    return Character( UnderlyingCharacterTable( chi ),
               cyc * ValuesOfClassFunction( chi ) );
    end );

InstallMethod( \*,
    "for class function, and cyclotomic",
    [ IsClassFunction, IsCyc ],
    function( chi, cyc )
    return ClassFunction( UnderlyingCharacterTable( chi ),
               ValuesOfClassFunction( chi ) * cyc );
    end );

InstallMethod( \*,
    "for virtual character, and integer",
    [ IsVirtualCharacter, IsInt ],
    function( chi, cyc )
    return VirtualCharacter( UnderlyingCharacterTable( chi ),
               ValuesOfClassFunction( chi ) * cyc );
    end );

InstallMethod( \*,
    "for character, and positive integer",
    [ IsCharacter, IsPosInt ],
    function( chi, cyc )
    return Character( UnderlyingCharacterTable( chi ),
               ValuesOfClassFunction( chi ) * cyc );
    end );


#############################################################################
##
#M  OneOp( <psi> )  . . . . . . . . . . . . . . . . . .  for a class function
##
InstallMethod( OneOp,
    "for class function",
    [ IsClassFunction ],
    psi -> TrivialCharacter( UnderlyingCharacterTable( psi ) ) );


#############################################################################
##
#M  \*( <chi>, <psi> )  . . . . . . . . . . tensor product of class functions
##
InstallMethod( \*,
    "for two class functions",
    [ IsClassFunction, IsClassFunction ],
    function( chi, psi )
    local tbl, valschi, valspsi;
    tbl:= UnderlyingCharacterTable( chi );
    if not IsIdenticalObj( tbl, UnderlyingCharacterTable( psi ) ) then
      Error( "no product of class functions of different tables" );
    fi;
    valschi:= ValuesOfClassFunction( chi );
    valspsi:= ValuesOfClassFunction( psi );
    return ClassFunction( tbl,
               List( [ 1 .. Length( valschi ) ],
                     x -> valschi[x] * valspsi[x] ) );
    end );

InstallMethod( \*,
    "for two virtual characters",
    IsIdenticalObj,
    [ IsVirtualCharacter, IsVirtualCharacter ],
    function( chi, psi )
    local tbl, valschi, valspsi;
    tbl:= UnderlyingCharacterTable( chi );
    if not IsIdenticalObj( tbl, UnderlyingCharacterTable( psi ) ) then
      Error( "no product of class functions of different tables" );
    fi;
    valschi:= ValuesOfClassFunction( chi );
    valspsi:= ValuesOfClassFunction( psi );
    return VirtualCharacter( tbl,
               List( [ 1 .. Length( valschi ) ],
                     x -> valschi[x] * valspsi[x] ) );
    end );

InstallMethod( \*,
    "for two characters",
    IsIdenticalObj,
    [ IsCharacter, IsCharacter ],
    function( chi, psi )
    local tbl, valschi, valspsi;
    tbl:= UnderlyingCharacterTable( chi );
    if not IsIdenticalObj( tbl, UnderlyingCharacterTable( psi ) ) then
      Error( "no product of class functions of different tables" );
    fi;
    valschi:= ValuesOfClassFunction( chi );
    valspsi:= ValuesOfClassFunction( psi );
    return Character( tbl,
               List( [ 1 .. Length( valschi ) ],
                     x -> valschi[x] * valspsi[x] ) );
    end );


#############################################################################
##
#M  \*( <chi>, <list> ) . . . . . . . . . . class function times default list
#M  \*( <list>, <chi> ) . . . . . . . . . . default list times class function
##
InstallOtherMethod( \*,
    "for class function, and list in `IsListDefault'",
    [ IsClassFunction, IsListDefault ],
    function( chi, list )
    return List( list, entry -> chi * entry );
    end );

InstallOtherMethod( \*,
    "for list in `IsListDefault', and class function",
    [ IsListDefault, IsClassFunction ],
    function( list, chi )
    return List( list, entry -> entry * chi );
    end );


#############################################################################
##
#M  Order( <chi> )  . . . . . . . . . . . . . . . . order of a class function
##
##  Note that we are not allowed to regard the determinantal order of an
##  arbitrary (virtual) character as its order,
##  since nonlinear characters do not have an order as mult. elements.
##
InstallMethod( Order,
    "for a class function",
    [ IsClassFunction ],
    function( chi )
    local order, values;

    values:= ValuesOfClassFunction( chi );
    if   0 in values then
      Error( "<chi> is not invertible" );
    elif ForAny( values, cyc -> not IsIntegralCyclotomic( cyc )
                                or cyc * GaloisCyc( cyc, -1 ) <> 1 ) then
      return infinity;
    fi;
    order:= Conductor( values );
    if order mod 2 = 1 and ForAny( values, i -> i^order <> 1 ) then
      order:= 2*order;
    fi;
    return order;
    end );


#############################################################################
##
#M  InverseOp( <chi> )  . . . . . . . . . . . . . . . .  for a class function
##
InstallMethod( InverseOp,
    "for a class function",
    [ IsClassFunction ],
    function( chi )
    local values;

    values:= List( ValuesOfClassFunction( chi ), Inverse );
    if fail in values then
      return fail;
    elif HasIsCharacter( chi ) and IsCharacter( chi ) and values[1] = 1 then
      return Character( UnderlyingCharacterTable( chi ), values );
    else
      return ClassFunction( UnderlyingCharacterTable( chi ), values );
    fi;
    end );


#############################################################################
##
#M  \^( <chi>, <n> )  . . . . . . . . . . for class function and pos. integer
##
InstallOtherMethod( \^,
    "for class function and positive integer (pointwise powering)",
    [ IsClassFunction, IsPosInt ],
    function( chi, n )
    return ClassFunctionSameType( UnderlyingCharacterTable( chi ),
               chi,
               List( ValuesOfClassFunction( chi ), x -> x^n ) );
    end );


#############################################################################
##
#M  \^( <chi>, <g> )  . . . . .  conjugate class function under action of <g>
##
InstallMethod( \^,
    "for class function and group element",
    [ IsClassFunction, IsMultiplicativeElementWithInverse ],
    function( chi, g )
    local tbl, G, mtbl, pi, fus, inv, imgs;

    tbl:= UnderlyingCharacterTable( chi );
    if HasUnderlyingGroup( tbl ) then
      # 'chi' is an ordinary character.
      G:= UnderlyingGroup( tbl );
      if IsElmsColls( FamilyObj( g ), FamilyObj( G ) ) then
        return ClassFunctionSameType( tbl, chi,
               Permuted( ValuesOfClassFunction( chi ),
                         CorrespondingPermutations( tbl, chi, [ g ] )[1] ) );
      fi;
    elif HasOrdinaryCharacterTable( tbl ) then
      # 'chi' is a Brauer character.
      mtbl:= tbl;
      tbl:= OrdinaryCharacterTable( mtbl );
      if HasUnderlyingGroup( tbl ) then
        G:= UnderlyingGroup( tbl );
        if IsElmsColls( FamilyObj( g ), FamilyObj( G ) ) then
          pi:= CorrespondingPermutations( tbl, [ g ] )[1]^-1;
          fus:= GetFusionMap( mtbl, tbl );
          inv:= InverseMap( fus );
          imgs:= List( [ 1 .. Length( fus ) ], i -> inv[ fus[i]^pi ] );
          return ClassFunctionSameType( mtbl, chi,
                 ValuesOfClassFunction( chi ){ imgs } );
        fi;
      fi;
    fi;
    TryNextMethod();
    end );


#############################################################################
##
#M  \^( <chi>, <galaut> ) . . . Galois automorphism <galaut> applied to <chi>
##
InstallOtherMethod( \^,
    "for class function and Galois automorphism",
    [ IsClassFunction, IsGeneralMapping ],
    function( chi, galaut )
    if IsANFAutomorphismRep( galaut ) then
      galaut:= galaut!.galois;
      return ClassFunctionSameType( UnderlyingCharacterTable( chi ), chi,
                 List( ValuesOfClassFunction( chi ),
                       x -> GaloisCyc( x, galaut ) ) );
    elif IsOne( galaut ) then
      return chi;
    else
      TryNextMethod();
    fi;
    end );


#############################################################################
##
#M  \^( <chi>, <G> )  . . . . . . . . . . . . . . . .  induced class function
#M  \^( <chi>, <tbl> )  . . . . . . . . . . . . . . .  induced class function
##
InstallOtherMethod( \^,
    "for class function and group",
    [ IsClassFunction, IsGroup ],
    InducedClassFunction );

InstallOtherMethod( \^,
    "for class function and nearly character table",
    [ IsClassFunction, IsNearlyCharacterTable ],
    InducedClassFunction );


#############################################################################
##
#M  \^( <g>, <chi> )  . . . . . . . . . . value of <chi> on group element <g>
##
InstallOtherMethod( \^,
    [ IsMultiplicativeElementWithInverse, IsClassFunction ],
    function( g, chi )
    local tbl, mtbl, ccl, i;

    tbl:= UnderlyingCharacterTable( chi );
    if HasOrdinaryCharacterTable( tbl ) then
      # 'chi' is a Brauer character.
      mtbl:= tbl;
      tbl:= OrdinaryCharacterTable( mtbl );
      if not HasUnderlyingGroup( tbl ) then
        Error( "table <tbl> of <chi> does not store its group" );
      elif not g in UnderlyingGroup( tbl )
           or Order( g ) mod UnderlyingCharacteristic( mtbl ) = 0 then
        Error( "<g> must be p-regular and lie in the underlying group of <chi>" );
      else
        ccl:= ConjugacyClasses( tbl ){ GetFusionMap( mtbl, tbl ) };
      fi;
    elif not HasUnderlyingGroup( tbl ) then
      Error( "table <tbl> of <chi> does not store its group" );
    elif not g in UnderlyingGroup( tbl ) then
      Error( "<g> must lie in the underlying group of <chi>" );
    else
      ccl:= ConjugacyClasses( tbl );
    fi;

    for i in [ 1 .. Length( ccl ) ] do
      if g in ccl[i] then
        return ValuesOfClassFunction( chi )[i];
      fi;
    od;
    end );


#############################################################################
##
#M  \^( <psi>, <chi> )  . . . . . . . . . .  conjugation of linear characters
##
InstallOtherMethod( \^,
    "for two class functions (conjugation, trivial action)",
    [ IsClassFunction, IsClassFunction ],
    ReturnFirst);

#############################################################################
##
#M  GlobalPartitionOfClasses( <tbl> )
##
InstallMethod( GlobalPartitionOfClasses,
    "for an ordinary character table",
    [ IsOrdinaryTable ],
    function( tbl )
    local part,     # partition that has to be respected
          list,     # list of all maps to be respected
          map,      # one map in 'list'
          inv,      # contains number of root classes
          newpart,  #
          values,   #
          j,        # loop over 'orb'
          pt;       # one point to map

    if HasAutomorphismsOfTable( tbl ) then

      # The orbits define the finest possible global partition.
      part:= OrbitsDomain( AutomorphismsOfTable( tbl ),
                     [ 1 .. Length( NrConjugacyClasses( tbl ) ) ] );

    else

      # Conjugate classes must have same representative order and
      # same centralizer order.
      list:= [ OrdersClassRepresentatives( tbl ),
               SizesCentralizers( tbl ) ];

      # The number of root classes is by definition invariant under
      # table automorphisms.
      for map in Compacted( ComputedPowerMaps( tbl ) ) do
        inv:= ZeroMutable( map );
        for j in map do
          inv[j]:= inv[j] + 1;
        od;
        Add( list, inv );
      od;

      # All elements in `list' must be respected.
      # Transform each of them into a partition,
      # and form the intersection of these partitions.
      part:= Partition( [ [ 1 .. Length( list[1] ) ] ] );
      for map in list do
        newpart := [];
        values  := [];
        for j in [ 1 .. Length( map ) ] do
          pt:= Position( values, map[j] );
          if pt = fail then
            Add( values, map[j] );
            Add( newpart, [ j ] );
          else
            Add( newpart[ pt ], j );
          fi;
        od;
        StratMeetPartition( part, Partition( newpart ) );
      od;
      part:= List( Cells( part ), Set );
#T unfortunately `Set' necessary ...

    fi;

    return part;
    end );


#############################################################################
##
#M  CorrespondingPermutations( <tbl>, <elms> )  . action on the conj. classes
##
InstallMethod( CorrespondingPermutations,
    "for character table and list of group elements",
    [ IsOrdinaryTable, IsHomogeneousList ],
    function( tbl, elms )
    local classes,  # list of conjugacy classes
          perms,    # list of permutations, result
          part,     # partition that has to be respected
          base,     # base of aut. group
          g,        # loop over `elms'
          images,   # list of images
          pt,       # one point to map
          im,       # actual image class
          orb,      # possible image points
          found,    # image point found? (boolean value)
          j,        # loop over 'orb'
          list,     # one list in 'part'
          first,    # first point in orbit of 'g'
          min;      # minimal length of nontrivial orbit of 'g'

    classes:= ConjugacyClasses( tbl );

    perms:= [];

    # If the table automorphisms are known then we only must determine
    # the images of a base of this group.
    if HasAutomorphismsOfTable( tbl ) then

      part:= AutomorphismsOfTable( tbl );

      if IsTrivial( part ) then
        return ListWithIdenticalEntries( Length( elms ), () );
      fi;

      # Compute the images of the base points of this group.
      base:= BaseOfGroup( part );

      for g in elms do

        if IsOne( g ) then

          # If `g' is the identity then nothing is to do.
          Add( perms, () );

        else

          images:= [];
          for pt in base do

            im:= Representative( classes[ pt ] ) ^ g;
            found:= false;
            for j in Orbit( part, pt ) do
#T better CanonicalClassElement ??
              if im in classes[j] then
                Add( images, j );
                found:= true;
                break;
              fi;
            od;

          od;

          # Compute a group element.
          Add( perms,
               RepresentativeAction( part, base, images, OnTuples ) );

        fi;

      od;

    else

      # We can use only a partition into unions of orbits.

      part:= GlobalPartitionOfClasses( tbl );
      if Length( part ) = Length( classes ) then
        return ListWithIdenticalEntries( Length( elms ), () );
      fi;

      for g in elms do
#T It would be more natural to write
#T Permutation( g, ConjugacyClasses( tbl ), OnPoints ),
#T *BUT* the `Permutation' method in question first asks whether
#T the list of classes is sorted,
#T and there is no method to compare the classes!

        if IsOne( g ) then

          # If `g' is the identity then nothing is to do.
          Add( perms, () );

        else

          # Compute orbits of `g' on the lists in `part', store the images.
          # Note that if we have taken away a union of orbits such that the
          # number of remaining points is smaller than the smallest prime
          # divisor of the order of `g' then all these points must be fixed.
          min:= Factors(Integers, Order( g ) )[1];
          images:= [];

          for list in part do

            if Length( list ) = 1 then
#T why not `min' ?

              # necessarily fixed point
              images[ list[1] ]:= list[1];

            else

              orb:= ShallowCopy( list );
              while min <= Length( orb ) do

                # There may be nontrivial orbits.
                pt:= orb[1];
                first:= pt;
                j:= 1;

                while j <= Length( orb ) do

                  im:= Representative( classes[ pt ] ) ^ g;
                  found:= false;
                  while j <= Length( orb ) and not found do
#T better CanonicalClassElement ??
                    if im in classes[ orb[j] ] then
                      images[pt]:= orb[j];
                      found:= true;
                    fi;
                    j:= j+1;
                  od;

                  RemoveSet( orb, pt );

                  if found and pt <> images[ pt ] then
                    pt:= images[ pt ];
                    j:= 1;
                  fi;

                od;

                # The image must be the first point of the orbit under `g'.
                images[pt]:= first;

              od;

              # The remaining points of the orbit must be fixed.
              for pt in orb do
                images[pt]:= pt;
              od;

            fi;

          od;

          # Compute a group element.
          Add( perms, PermList( images ) );

        fi;

      od;

    fi;

    return perms;
    end );


#############################################################################
##
#M  CorrespondingPermutations( <tbl>, <chi>, <elms> )
##
InstallOtherMethod( CorrespondingPermutations,
    "for a char. table, a hom. list, and a list of group elements",
    [ IsOrdinaryTable, IsHomogeneousList, IsHomogeneousList ],
    function( tbl, values, elms )
    local classes,  # list of conjugacy classes
          perms,    # list of permutations, result
          part,     # partition that has to be respected
          base,     # base of aut. group
          g,        # loop over `elms'
          images,   # list of images
          pt,       # one point to map
          im,       # actual image class
          orb,      # possible image points
          found,    # image point found? (boolean value)
          j,        # loop over 'orb'
          list,     # one list in 'part'
          first,    # first point in orbit of 'g'
          min;      # minimal length of nontrivial orbit of 'g'

    classes:= ConjugacyClasses( tbl );
    perms:= [];

    # If the table automorphisms are known then we only must determine
    # the images of a base of this group.
    if HasAutomorphismsOfTable( tbl ) then

      part:= AutomorphismsOfTable( tbl );

      if IsTrivial( part ) then
        return ListWithIdenticalEntries( Length( elms ), () );
      fi;

      # Compute the images of the base points of this group.
      base:= BaseOfGroup( part );

      for g in elms do

        if IsOne( g ) then

          # If `g' is the identity then nothing is to do.
          Add( perms, () );

        else

          images:= [];
          for pt in base do

            im:= Representative( classes[ pt ] ) ^ g;
            found:= false;
            for j in Orbit( part, pt ) do
#T better CanonicalClassElement ??
              if im in classes[j] then
                Add( images, j );
                found:= true;
                break;
              fi;
              j:= j+1;
            od;

          od;

          # Compute a group element (if possible).
          Add( perms,
               RepresentativeAction( part, base, images, OnTuples ) );

        fi;

      od;

    else

      # We can use only a partition into unions of orbits.

      part:= GlobalPartitionOfClasses( tbl );
      if Length( part ) = Length( classes ) then
        return ListWithIdenticalEntries( Length( elms ), () );
      fi;

      for g in elms do

        if IsOne( g ) then

          # If `g' is the identity then nothing is to do.
          Add( perms, () );

        else

          # Compute orbits of `g' on the lists in `part', store the images.
          # Note that if we have taken away a union of orbits such that the
          # number of remaining points is smaller than the smallest prime
          # divisor of the order of `g' then all these points must be fixed.
          min:= Factors(Integers, Order( g ) )[1];
          images:= [];

          for list in part do

            if Length( list ) = 1 then
#T why not `min' ?

              # necessarily fixed point
              images[ list[1] ]:= list[1];

            elif Length( Set( values{ list } ) ) = 1 then

              # We may take any permutation of the orbit.
              for j in list do
                images[j]:= j;
              od;

            else

              orb:= ShallowCopy( list );
              while Length( orb ) >= min do
#T fishy for S4 acting on V4 !!

                # There may be nontrivial orbits.
                pt:= orb[1];
                first:= pt;
                j:= 1;

                while j <= Length( orb ) do

                  im:= Representative( classes[ pt ] ) ^ g;
                  found:= false;
                  while j <= Length( orb ) and not found do
#T better CanonicalClassElement ??
                    if im in classes[ orb[j] ] then
                      images[pt]:= orb[j];
                      found:= true;
                    fi;
                    j:= j+1;
                  od;

                  RemoveSet( orb, pt );

                  if found then
                    j:= 1;
                    pt:= images[pt];
                  fi;

                od;

                # The image must be the first point of the orbit under `g'.
                images[pt]:= first;

              od;

              # The remaining points of the orbit must be fixed.
              for pt in orb do
                images[pt]:= pt;
              od;

            fi;

          od;

          # Compute a group element.
          Add( perms, PermList( images ) );

        fi;

      od;

    fi;

    return perms;
    end );


#############################################################################
##
#M  ComplexConjugate( <chi> )
##
##  We use `InstallOtherMethod' because class functions are both scalars and
##  lists, so the method matches two declarations of the operation.
##
InstallOtherMethod( ComplexConjugate,
    "for a class function",
    [ IsClassFunction and IsCyclotomicCollection ],
    chi -> GaloisCyc( chi, -1 ) );


#############################################################################
##
#M  GaloisCyc( <chi>, <k> )
##
InstallMethod( GaloisCyc,
    "for a class function, and an integer",
    [ IsClassFunction and IsCyclotomicCollection, IsInt ],
    function( chi, k )
    local tbl, char, n, g;

    tbl:= UnderlyingCharacterTable( chi );
    char:= UnderlyingCharacteristic( tbl );
    n:= Conductor( chi );
    g:= Gcd( k, n );

    if k = -1 or
       ( char = 0 and g = 1 ) then
      return ClassFunctionSameType( tbl, chi,
                 GaloisCyc( ValuesOfClassFunction( chi ), k ) );
#T also if k acts as some *(p^d) for char = p
#T (reduce k mod n, and then what?)
    else
      return ClassFunction( tbl,
                 GaloisCyc( ValuesOfClassFunction( chi ), k ) );
    fi;
    end );


#############################################################################
##
#M  Permuted( <chi>, <perm> )
##
InstallMethod( Permuted,
    "for a class function, and a permutation",
    [ IsClassFunction, IsPerm ],
    function( chi, perm )
    return ClassFunction( UnderlyingCharacterTable( chi ),
               Permuted( ValuesOfClassFunction( chi ), perm ) );
    end );


#############################################################################
##
##  5. Printing Class Functions
##

#############################################################################
##
#M  ViewObj( <psi> )  . . . . . . . . . . . . . . . . . view a class function
##
##  Note that class functions are finite lists, so the default `ViewObj'
##  method for finite lists should be avoided.
##
InstallMethod( ViewObj,
    "for a class function",
    [ IsClassFunction ],
    function( psi )
    Print( "ClassFunction( " );
    View( UnderlyingCharacterTable( psi ) );
    Print( ",\<\<\<\>\>\> " );
    View( ValuesOfClassFunction( psi ) );
    Print( " )" );
    end );

InstallMethod( ViewObj,
    "for a virtual character",
    [ IsClassFunction and IsVirtualCharacter ],
    function( psi )
    Print( "VirtualCharacter( " );
    View( UnderlyingCharacterTable( psi ) );
    Print( ",\<\<\<\>\>\> " );
    View( ValuesOfClassFunction( psi ) );
    Print( " )" );
    end );

InstallMethod( ViewObj,
    "for a character",
    [ IsClassFunction and IsCharacter ],
    function( psi )
    Print( "Character( " );
    View( UnderlyingCharacterTable( psi ) );
    Print( ",\<\<\<\>\>\> " );
    View( ValuesOfClassFunction( psi ) );
    Print( " )" );
    end );


#############################################################################
##
#M  PrintObj( <psi> ) . . . . . . . . . . . . . . . .  print a class function
##
InstallMethod( PrintObj,
    "for a class function",
    [ IsClassFunction ],
    function( psi )
    Print( "ClassFunction( ", UnderlyingCharacterTable( psi ),
           ", ", ValuesOfClassFunction( psi ), " )" );
    end );

InstallMethod( PrintObj,
    "for a virtual character",
    [ IsClassFunction and IsVirtualCharacter ],
    function( psi )
    Print( "VirtualCharacter( ", UnderlyingCharacterTable( psi ),
           ", ", ValuesOfClassFunction( psi ), " )" );
    end );

InstallMethod( PrintObj,
    "for a character",
    [ IsClassFunction and IsCharacter ],
    function( psi )
    Print( "Character( ", UnderlyingCharacterTable( psi ),
           ", ", ValuesOfClassFunction( psi ), " )" );
    end );


#############################################################################
##
#M  Display( <chi> )  . . . . . . . . . . . . . . .  display a class function
#M  Display( <chi>, <arec> )
##
InstallMethod( Display,
    "for a class function",
    [ IsClassFunction ],
    function( chi )
    Display( UnderlyingCharacterTable( chi ), rec( chars:= [ chi ] ) );
    end );

InstallOtherMethod( Display,
    "for a class function, and a record",
    [ IsClassFunction, IsRecord ],
    function( chi, arec )
    arec:= ShallowCopy( arec );
    arec.chars:= [ chi ];
    Display( UnderlyingCharacterTable( chi ), arec );
    end );


#############################################################################
##
##  6. Creating Class Functions from Values Lists
##


#############################################################################
##
#M  ClassFunction( <tbl>, <values> )
##
InstallMethod( ClassFunction,
    "for nearly character table, and dense list",
    [ IsNearlyCharacterTable, IsDenseList ],
    function( tbl, values )
    local chi;

    # Check the no. of classes.
    if NrConjugacyClasses( tbl ) <> Length( values ) then
      Error( "no. of classes in <tbl> and <values> must be equal" );
    fi;

    # Create the object.
    chi:= Objectify( NewType( FamilyObj( values ),
                                  IsClassFunction
                              and IsAttributeStoringRep ),
                     rec() );

    # Store the defining attribute values.
    SetValuesOfClassFunction( chi, ValuesOfClassFunction( values ) );
    SetUnderlyingCharacterTable( chi, tbl );

    # Store useful information.
    if IsSmallList( values ) then
      SetIsSmallList( chi, true );
    fi;

    return chi;
    end );


#############################################################################
##
#M  ClassFunction( <G>, <values> )
##
InstallMethod( ClassFunction,
    "for a group, and a dense list",
    [ IsGroup, IsDenseList ],
    function( G, values )
    return ClassFunction( OrdinaryCharacterTable( G ), values );
    end );


#############################################################################
##
#M  VirtualCharacter( <tbl>, <values> )
##
InstallMethod( VirtualCharacter,
    "for nearly character table, and dense list",
    [ IsNearlyCharacterTable, IsDenseList ],
    function( tbl, values )
    values:= ClassFunction( tbl, values );
    SetIsVirtualCharacter( values, true );
    return values;
    end );


#############################################################################
##
#M  VirtualCharacter( <G>, <values> )
##
InstallMethod( VirtualCharacter,
    "for a group, and a dense list",
    [ IsGroup, IsDenseList ],
    function( G, values )
    return VirtualCharacter( OrdinaryCharacterTable( G ), values );
    end );


#############################################################################
##
#M  Character( <tbl>, <values> )
##
InstallMethod( Character,
    "for nearly character table, and dense list",
    [ IsNearlyCharacterTable, IsDenseList ],
    function( tbl, values )
    values:= ClassFunction( tbl, values );
    SetIsCharacter( values, true );
    return values;
    end );


#############################################################################
##
#M  Character( <G>, <values> )
##
InstallMethod( Character,
    "for a group, and a dense list",
    [ IsGroup, IsDenseList ],
    function( G, values )
    return Character( OrdinaryCharacterTable( G ), values );
    end );


#############################################################################
##
#F  ClassFunctionSameType( <tbl>, <chi>, <values> )
##
InstallGlobalFunction( ClassFunctionSameType,
    function( tbl, chi, values )
    if not IsClassFunction( chi ) then
      return values;
    elif HasIsCharacter( chi ) and IsCharacter( chi ) then
      return Character( tbl, values );
    elif HasIsVirtualCharacter( chi ) and IsVirtualCharacter( chi ) then
      return VirtualCharacter( tbl, values );
    else
      return ClassFunction( tbl, values );
    fi;
end );


#############################################################################
##
##  7. Creating Class Functions using Groups
##


#############################################################################
##
#M  TrivialCharacter( <tbl> ) . . . . . . . . . . . . . for a character table
##
InstallMethod( TrivialCharacter,
    "for a character table",
    [ IsNearlyCharacterTable ],
    function( tbl )
    local chi;

    chi:= Character( tbl,
              ListWithIdenticalEntries( NrConjugacyClasses( tbl ), 1 ) );
    SetIsIrreducibleCharacter( chi, true );
    return chi;
    end );


#############################################################################
##
#M  TrivialCharacter( <G> ) . . . . . . . . . . . . . . . . . . . for a group
##
InstallMethod( TrivialCharacter,
    "for a group (delegate to the table)",
    [ IsGroup ],
    G -> TrivialCharacter( OrdinaryCharacterTable( G ) ) );


#############################################################################
##
#M  NaturalCharacter( <G> ) . . . . . . . . . . . . . for a permutation group
##
InstallMethod( NaturalCharacter,
    "for a permutation group",
    [ IsGroup and IsPermCollection ],
    function( G )
    local deg, tbl;
    deg:= NrMovedPoints( G );
    tbl:= OrdinaryCharacterTable( G );
    return Character( tbl,
               List( ConjugacyClasses( tbl ),
               C -> deg - NrMovedPoints( Representative( C ) ) ) );
    end );


#############################################################################
##
#M  NaturalCharacter( <G> ) . . . . for a matrix group in characteristic zero
##
InstallMethod( NaturalCharacter,
    "for a matrix group in characteristic zero",
    [ IsGroup and IsRingElementCollCollColl ],
    function( G )
    local tbl;
    if Characteristic( G ) = 0 then
      tbl:= OrdinaryCharacterTable( G );
      return Character( tbl,
                 List( ConjugacyClasses( tbl ),
                       C -> TraceMat( Representative( C ) ) ) );
    else
      TryNextMethod();
    fi;
    end );


#############################################################################
##
#M  NaturalCharacter( <hom> ) . . . . . . . . . . .  for a group homomorphism
##
##  We use shortcuts for homomorphisms onto permutation groups and matrix
##  groups in characteristic zero,
##  since the meaning of `NaturalCharacter' is clear for these cases and
##  we can avoid explicit conjugacy tests in the image.
##  For other cases, we use a generic way.
##
InstallMethod( NaturalCharacter,
    "for a group general mapping",
    [ IsGeneralMapping ],
    function( hom )
    local G, R, deg, tbl, chi;
    G:= Source( hom );
    R:= Range( hom );
    tbl:= OrdinaryCharacterTable( G );
    if IsPermGroup( R ) then
      deg:= NrMovedPoints( R );
      return Character( tbl,
                 List( ConjugacyClasses( tbl ),
                 C -> deg - NrMovedPoints( ImagesRepresentative( hom,
                                Representative( C ) ) ) ) );
    elif IsMatrixGroup( R ) and Characteristic( R ) = 0 then
      return Character( tbl,
                 List( ConjugacyClasses( tbl ),
                 C -> TraceMat( ImagesRepresentative( hom,
                          Representative( C ) ) ) ) );
    else
      chi:= NaturalCharacter( Image( hom ) );
      return Character( tbl,
                 List( ConjugacyClasses( tbl ),
                 C -> ImagesRepresentative( hom,
                          Representative( C ) ) ^ chi ) );
    fi;
    end );


#############################################################################
##
#M  PermutationCharacter( <G>, <D>, <opr> ) . . . . . . . .  for group action
##
InstallMethod( PermutationCharacter,
    "group action on domain",
    [ IsGroup, IsCollection, IsFunction ],
    function( G, dom, opr )
    local tbl;
    tbl:= OrdinaryCharacterTable( G );
    return Character( tbl, List( ConjugacyClasses( tbl ),
               i -> Number( dom, j -> j = opr( j, Representative(i) ) ) ) );
    end);


#############################################################################
##
#M  PermutationCharacter( <G>, <U> )  . . . . . . . . . . . .  for two groups
##
InstallMethod( PermutationCharacter,
    "for two groups (use double cosets)",
    IsIdenticalObj,
    [ IsGroup, IsGroup ],
    function( G, U )
    local tbl, C, c, s, i;

    tbl:= OrdinaryCharacterTable( G );
    C := ConjugacyClasses( tbl );
    c := [ Index( G, U ) ];
    s := Size( U );

    for i  in [ 2 .. Length(C) ]  do
      c[i]:= Number( DoubleCosets( G, U,
                         SubgroupNC( G, [ Representative( C[i] ) ] ) ),
                     x -> Size( x ) = s );
    od;

    # Return the character.
    return Character( tbl, c );
    end );


#T #############################################################################
#T ##
#T #M  PermutationCharacter( <G>, <U> )  . . . . . . . . .  for two small groups
#T ##
#T InstallMethod( PermutationCharacter,
#T     "for two small groups",
#T     IsIdenticalObj,
#T     [ IsGroup and IsSmallGroup, IsGroup and IsSmallGroup ],
#T     function( G, U )
#T     local E, I, tbl;
#T
#T     E := AsList( U );
#T     I := Size( G ) / Length( E );
#T     tbl:= OrdinaryCharacterTable( G );
#T     return Character( tbl,
#T         List( ConjugacyClasses( tbl ),
#T         C -> I * Length( Intersection2( AsList( C ), E ) ) / Size( C ) ) );
#T     end );


#############################################################################
##
##  8. Operations for Class Functions
##


#############################################################################
##
#M  IsCharacter( <obj> )  . . . . . . . . . . . . . . for a virtual character
##
InstallMethod( IsCharacter,
    "for a virtual character",
    [ IsClassFunction and IsVirtualCharacter ],
    obj -> IsCharacter( UnderlyingCharacterTable( obj ),
                        ValuesOfClassFunction( obj ) ) );

InstallMethod( IsCharacter,
    "for a class function",
    [ IsClassFunction ],
    function( obj )
    if HasIsVirtualCharacter( obj ) and not IsVirtualCharacter( obj ) then
#T can disappear when inverse implications are supported!
      return false;
    fi;
    return IsCharacter( UnderlyingCharacterTable( obj ),
                        ValuesOfClassFunction( obj ) );
    end );

InstallMethod( IsCharacter,
    "for an ordinary character table, and a homogeneous list",
    [ IsOrdinaryTable, IsHomogeneousList ],
    function( tbl, list )
    local chi, scpr;

    # Proper characters have positive degree.
    if list[1] <= 0 then
      return false;
    fi;

    # Check the scalar products with all irreducibles.
    for chi in Irr( tbl ) do
      scpr:= ScalarProduct( tbl, chi, list );
      if not IsInt( scpr ) or scpr < 0 then
        return false;
      fi;
    od;
    return true;
    end );

InstallMethod( IsCharacter,
    "for a Brauer table, and a homogeneous list",
    [ IsBrauerTable, IsHomogeneousList ],
    function( tbl, list )
    # Proper characters have positive degree.
    if list[1] <= 0 then
      return false;
    fi;

    # Check the decomposition.
    return Decomposition( Irr( tbl ), [ list ], "nonnegative" )[1] <> fail;
    end );


#############################################################################
##
#M  IsVirtualCharacter( <chi> ) . . . . . . . . . . . .  for a class function
##
InstallMethod( IsVirtualCharacter,
    "for a class function",
    [ IsClassFunction ],
    chi -> IsVirtualCharacter( UnderlyingCharacterTable( chi ),
                               ValuesOfClassFunction( chi ) ) );

InstallMethod( IsVirtualCharacter,
    "for an ordinary character table, and a homogeneous list",
    [ IsOrdinaryTable, IsHomogeneousList ],
    function( tbl, list )
    local chi;

    # Check the scalar products with all irreducibles.
    for chi in Irr( tbl ) do
      if not IsInt( ScalarProduct( tbl, chi, list ) ) then
        return false;
      fi;
    od;
    return true;
    end );

# InstallMethod( IsVirtualCharacter,
#     "for a Brauer table, and a homogeneous list",
#     [ IsBrauerTable, IsHomogeneousList ],
#     function( tbl, list )
#     ???
#     end );


#############################################################################
##
#M  IsIrreducibleCharacter( <chi> )   . . . . . . . . .  for a class function
##
InstallMethod( IsIrreducibleCharacter,
    "for a class function",
    [ IsClassFunction ],
    chi -> IsIrreducibleCharacter( UnderlyingCharacterTable( chi ),
                                   ValuesOfClassFunction( chi ) ) );

InstallMethod( IsIrreducibleCharacter,
    "for an ordinary character table, and a homogeneous list",
    [ IsOrdinaryTable, IsHomogeneousList ],
    function( tbl, list )
    return     IsVirtualCharacter( tbl, list )
           and ScalarProduct( tbl, list, list) = 1
           and list[1] > 0;
    end );

InstallMethod( IsIrreducibleCharacter,
    "for a Brauer table, and a homogeneous list",
    [ IsBrauerTable, IsHomogeneousList ],
    function( tbl, list )
    local i, found;
    list:= Decomposition( Irr( tbl ), [ list ], "nonnegative" )[1];
    if list = fail then
      return false;
    fi;
    found:= false;
    for i in list do
      if i <> 0 then
        if found or i <> 1 then
          return false;
        else
          found:= true;
        fi;
      fi;
    od;
    return found;
    end );


#############################################################################
##
#M  ScalarProduct( <chi>, <psi> ) . . . . . . . . . . for two class functions
##
InstallMethod( ScalarProduct,
    "for two class functions",
    [ IsClassFunction, IsClassFunction ],
    function( chi, psi )
    local tbl;

    tbl:= UnderlyingCharacterTable( chi );
    if tbl <> UnderlyingCharacterTable( psi ) then
      Error( "<chi> and <psi> have different character tables" );
    fi;
    return ScalarProduct( tbl, ValuesOfClassFunction( chi ),
                               ValuesOfClassFunction( psi ) );
    end );


#############################################################################
##
#M  ScalarProduct( <tbl>, <chi>, <psi> ) .  scalar product of class functions
##
InstallMethod( ScalarProduct,
    "for character table and two homogeneous lists",
    [ IsCharacterTable, IsRowVector, IsRowVector ],
    function( tbl, x1, x2 )
    local i,       # loop variable
          scpr,    # scalar product, result
          weight;  # lengths of conjugacy classes

    weight:= SizesConjugacyClasses( tbl );
    x1:= ValuesOfClassFunction( x1 );
    x2:= ValuesOfClassFunction( x2 );
    scpr:= 0;
    for i in [ 1 .. Length( x1 ) ] do
      if x1[i]<>0 and x2[i]<>0 then
        scpr:= scpr + x1[i] * GaloisCyc( x2[i], -1 ) * weight[i];
      fi;
    od;
    return scpr / Size( tbl );
    end );


#############################################################################
##
#F  MatScalarProducts( [<tbl>, ]<list1>, <list2> )
#F  MatScalarProducts( [<tbl>, ]<list> )
##
InstallMethod( MatScalarProducts,
    "for two homogeneous lists",
    [ IsHomogeneousList, IsHomogeneousList ],
    function( list1, list2 )
    if IsEmpty( list1 ) then
      return [];
    elif not IsClassFunction( list1[1] ) then
      Error( "<list1> must consist of class functions" );
    else
      return MatScalarProducts( UnderlyingCharacterTable( list1[1] ),
                                list1, list2 );
    fi;
    end );

InstallMethod( MatScalarProducts,
    "for a homogeneous list",
    [ IsHomogeneousList ],
    function( list )
    if IsEmpty( list ) then
      return [];
    elif not IsClassFunction( list[1] ) then
      Error( "<list> must consist of class functions" );
    else
      return MatScalarProducts( UnderlyingCharacterTable( list[1] ), list );
    fi;
    end );

InstallMethod( MatScalarProducts,
    "for an ordinary table, and two homogeneous lists",
    [ IsOrdinaryTable, IsHomogeneousList, IsHomogeneousList ],
    function( tbl, list1, list2 )
    local i, j, chi, nccl, weight, scprmatrix, order, scpr;

    if IsEmpty( list1 ) then
      return [];
    fi;
    list1:= List( list1, ValuesOfClassFunction );

    nccl:= NrConjugacyClasses( tbl );
    weight:= SizesConjugacyClasses( tbl );
    order:= Size( tbl );

    scprmatrix:= [];
    for i in [ 1 .. Length( list2 ) ] do
      scprmatrix[i]:= [];
      chi:= List( ValuesOfClassFunction( list2[i] ), x -> GaloisCyc(x,-1) );
      for j in [ 1 .. nccl ] do
        chi[j]:= chi[j] * weight[j];
      od;
      for j in list1 do
        scpr:= ( chi * j ) / order;
        Add( scprmatrix[i], scpr );
        if not IsInt( scpr ) then
          if IsRat( scpr ) then
            Info( InfoCharacterTable, 2,
                  "MatScalarProducts: sum not divisible by group order" );
          elif IsCyc( scpr ) then
            Info( InfoCharacterTable, 2,
                  "MatScalarProducts: summation not integer valued");
          fi;
        fi;
      od;
    od;
    return scprmatrix;
    end );

InstallMethod( MatScalarProducts,
    "for an ordinary table, and a homogeneous list",
    [ IsOrdinaryTable, IsHomogeneousList ],
    function( tbl, list )
    local i, j, chi, nccl, weight, scprmatrix, order, scpr;

    if IsEmpty( list ) then
      return [];
    fi;
    list:= List( list, ValuesOfClassFunction );

    nccl:= NrConjugacyClasses( tbl );
    weight:= SizesConjugacyClasses( tbl );
    order:= Size( tbl );

    scprmatrix:= [];
    for i in [ 1 .. Length( list ) ] do
      scprmatrix[i]:= [];
      chi:= List( list[i], x -> GaloisCyc( x, -1 ) );
      for j in [ 1 .. nccl ] do
        chi[j]:= chi[j] * weight[j];
      od;
      for j in [ 1 .. i ] do
        scpr:= ( chi * list[j] ) / order;
        Add( scprmatrix[i], scpr );
        if not IsInt( scpr ) then
          if IsRat( scpr ) then
            Info( InfoCharacterTable, 2,
                  "MatScalarProducts: sum not divisible by group order" );
          elif IsCyc( scpr ) then
            Info( InfoCharacterTable, 2,
                  "MatScalarProducts: summation not integer valued");
          fi;
        fi;
      od;
    od;
    return scprmatrix;
    end );


#############################################################################
##
#M  Norm( [<tbl>, ]<chi> ) . . . . . . . . . . . . . . norm of class function
##
InstallOtherMethod( Norm,
    "for a class function",
    [ IsClassFunction ],
    chi -> ScalarProduct( chi, chi ) );

InstallOtherMethod( Norm,
    "for an ordinary character table and a homogeneous list",
    [ IsOrdinaryTable, IsHomogeneousList ],
    function( tbl, chi )
    return ScalarProduct( tbl, chi, chi );
    end );


#############################################################################
##
#M  CentreOfCharacter( [<tbl>, ]<chi> ) . . . . . . . . centre of a character
##
InstallMethod( CentreOfCharacter,
    "for a class function",
    [ IsClassFunction ],
    chi -> CentreOfCharacter( UnderlyingCharacterTable( chi ),
                              ValuesOfClassFunction( chi ) ) );

InstallMethod( CentreOfCharacter,
    "for an ordinary table, and a homogeneous list ",
    [ IsOrdinaryTable, IsHomogeneousList ],
    function( tbl, list )
    if not HasUnderlyingGroup( tbl ) then
      Error( "<tbl> does not store its group" );
    fi;
    return NormalSubgroupClasses( tbl, ClassPositionsOfCentre( list ) );
    end );


#############################################################################
##
#M  ClassPositionsOfCentre( <chi> )  . . classes in the centre of a character
##
##  We know that an algebraic integer $\alpha$ is a root of unity
##  if and only if all conjugates of $\alpha$ have absolute value at most 1.
##  Since $\alpha^{\ast} \overline{\alpha^{\ast}} = 1$ holds for a Galois
##  automorphism $\ast$ if and only if $\alpha \overline{\alpha} = 1$ holds,
##  a cyclotomic integer is a root of unity iff its absolute value is $1$.
##
##  Cf. the comment about the `Order' method for cyclotomics in the file
##  `lib/cyclotom.g'.
##
##  The `IsCyc' test is necessary to avoid errors in the case that <chi>
##  contains unknowns.
##
InstallMethod( ClassPositionsOfCentre,
    "for a homogeneous list",
    [ IsHomogeneousList ],
    function( chi )
    local deg, mdeg, degsquare;

    deg:= chi[1];
    mdeg:= - deg;
    degsquare:= deg^2;

    return PositionsProperty( chi,
               x -> x = deg or x = mdeg or
                    ( ( not IsInt( x ) ) and IsCyc( x ) and IsCycInt( x )
                      and x * GaloisCyc( x, -1 ) = degsquare ) );
    end );


#############################################################################
##
#M  ConstituentsOfCharacter( [<tbl>, ]<chi> ) .  irred. constituents of <chi>
##
InstallMethod( ConstituentsOfCharacter,
    [ IsClassFunction ],
    chi -> ConstituentsOfCharacter( UnderlyingCharacterTable( chi ), chi ) );

InstallMethod( ConstituentsOfCharacter,
    "for an ordinary table, and a character",
    [ IsOrdinaryTable, IsClassFunction and IsCharacter ],
    function( tbl, chi )
    local irr,    # irreducible characters of `tbl'
          values, # character values
          deg,    # degree of `chi'
          const,  # list of constituents, result
          i,      # loop over `irr'
          irrdeg, # degree of an irred. character
          scpr;   # one scalar product

    tbl:= UnderlyingCharacterTable( chi );
    irr:= Irr( tbl );
    values:= ValuesOfClassFunction( chi );
    deg:= values[1];
    const:= [];
    i:= 1;
    while 0 < deg and i <= Length( irr ) do
      irrdeg:= DegreeOfCharacter( irr[i] );
      if irrdeg <= deg then
        scpr:= ScalarProduct( tbl, chi, irr[i] );
        if scpr <> 0 then
          deg:= deg - scpr * irrdeg;
          Add( const, irr[i] );
        fi;
      fi;
      i:= i+1;
    od;

    return Set( const );
    end );

InstallMethod( ConstituentsOfCharacter,
    "for an ordinary table, and a homogeneous list",
    [ IsOrdinaryTable, IsHomogeneousList ],
    function( tbl, chi )
    local const,  # list of constituents, result
          proper, # is `chi' a proper character
          i,      # loop over `irr'
          scpr;   # one scalar product

    const:= [];
    proper:= true;
    for i in Irr( tbl ) do
      scpr:= ScalarProduct( tbl, chi, i );
      if scpr <> 0 then
        Add( const, i );
        proper:= proper and IsPosInt( scpr );
      fi;
    od;

    # In the case `proper = true' we know that `chi' is a character.
    if proper then
      SetIsCharacter( chi, true );
    fi;

    return Set( const );
    end );

InstallMethod( ConstituentsOfCharacter,
    "for a Brauer table, and a homogeneous list",
    [ IsBrauerTable, IsHomogeneousList ],
    function( tbl, chi )
    local irr, intA, intB, dec;

    irr:= Irr( tbl );
    intA:= IntegralizedMat( irr );
    intB:= IntegralizedMat( [ chi ], intA.inforec );
    dec:= SolutionIntMat( intA.mat, intB.mat[1] );
    if dec = fail then
      Error( "<chi> is not a virtual character of <tbl>" );
    fi;

    return SortedList( irr{ Filtered( [ 1 .. Length( dec ) ],
                                      i -> dec[i] <> 0 ) } );
    end );


#############################################################################
##
#M  DegreeOfCharacter( <chi> )  . . . . . . . . . . . .  for a class function
##
InstallMethod( DegreeOfCharacter,
    "for a class function",
    [ IsClassFunction ],
    chi -> ValuesOfClassFunction( chi )[1] );


#############################################################################
##
#M  InertiaSubgroup( [<tbl>, ]<G>, <chi> )  . inertia subgroup of a character
##
InstallMethod( InertiaSubgroup,
    "for a group, and a class function",
    [ IsGroup, IsClassFunction ],
    function( G, chi )
    return InertiaSubgroup( UnderlyingCharacterTable( chi ), G,
                            ValuesOfClassFunction( chi ) );
    end );

InstallMethod( InertiaSubgroup,
    "for an ordinary table, a group, and a homogeneous list",
    [ IsOrdinaryTable, IsGroup, IsHomogeneousList ],
    function( tbl, G, chi )
    local H,          # group of `chi'
          index,      # index of `H' in `G'
          induced,    # induced of `chi' from `H' to `G'
          global,     # global partition of classes
          part,       # refined partition
          p,          # one set in `global' and `part'
          val,        # one value in `p'
          values,     # list of character values on `p'
          new,        # list of refinements of `p'
          i,          # loop over stored partitions
          pos,        # position where to store new partition later
          perms,      # permutations corresp. to generators of `G'
          permgrp,    # group generated by `perms'
          stab;       # the inertia subgroup, result

    # `G' must normalize the group of `chi'.
    H:= UnderlyingGroup( tbl );
    if not ( IsSubset( G, H ) and IsNormal( G, H ) ) then
      Error( "<H> must be a normal subgroup in <G>" );
    fi;

    # For prime index, check the norm of the induced character.
    # (We get a decision if `chi' is irreducible.)
    index:= Index( G, H );
    if IsPrimeInt( index ) then
      induced:= InducedClassFunction( tbl, chi, G );
      if ScalarProduct( CharacterTable( G ), induced, induced ) = 1 then
        return H;
      elif ScalarProduct( tbl, chi, chi ) = 1 then
        return G;
      fi;
    fi;

    # Compute the partition that must be stabilized.
#T Why is `StabilizerPartition' no longer available?
#T In GAP 3.5, there was such a function.
    # (We need only those cells where `chi' really yields a refinement.)
    global:= GlobalPartitionOfClasses( tbl );

    part:= [];
    for p in global do
#T only if `p' has length > 1 !
      val:= chi[ p[1] ];
      if ForAny( p, x -> chi[x] <> val ) then

        # proper refinement will yield a condition.
        values:= [];
        new:= [];
        for i in p do
          pos:= Position( values, chi[i] );
          if pos = fail then
            Add( values, chi[i] );
            Add( new, [ i ] );
          else
            Add( new[ pos ], i );
          fi;
        od;
        Append( part, new );

      fi;
    od;

    # If no refinement occurs, the character is necessarily invariant in <G>.
    if IsEmpty( part ) then
      return G;
    fi;

    # Compute the permutations corresponding to the generators of `G'.
    perms:= CorrespondingPermutations( tbl, chi, GeneratorsOfGroup( G ) );
    permgrp:= GroupByGenerators( perms );

    # `G' acts on the set of conjugacy classes given by each cell of `part'.
    stab:= permgrp;
    for p in part do
      stab:= Stabilizer( stab, p, OnSets );
#T Better one step (partition stabilizer) ??
    od;

    # Construct and return the result.
    if stab = permgrp then
      return G;
    else
      return PreImagesSet( GroupHomomorphismByImages( G, permgrp,
                               GeneratorsOfGroup( G ), perms ),
                 stab );
    fi;
    end );


#############################################################################
##
#M  KernelOfCharacter( [<tbl>, ]<chi> ) . . . . . . . .  for a class function
##
InstallMethod( KernelOfCharacter,
    "for a class function",
    [ IsClassFunction ],
    chi -> KernelOfCharacter( UnderlyingCharacterTable( chi ),
                              ValuesOfClassFunction( chi ) ) );

InstallMethod( KernelOfCharacter,
    "for an ordinary table, and a homogeneous list",
    [ IsOrdinaryTable, IsHomogeneousList ],
    function( tbl, chi )
    return NormalSubgroupClasses( tbl, ClassPositionsOfKernel( chi ) );
    end );


#############################################################################
##
#M  ClassPositionsOfKernel( <char> ) .  the set of classes forming the kernel
##
InstallMethod( ClassPositionsOfKernel,
    "for a homogeneous list",
    [ IsHomogeneousList ],
    function( char )
    local degree;
    degree:= char[1];
    return Filtered( [ 1 .. Length( char ) ], x -> char[x] = degree );
    end );


#############################################################################
##
#M  CycleStructureClass( [<tbl>, ]<permchar>, <class> )
##
##  For a permutation character $\pi$ and an element $g$ of $G$, the number
##  of $n$-cycles in the underlying permutation representation is equal to
##  $\frac{1}{n} \sum_{r|n} \mu(\frac{n}{r}) \pi(g^r)$.
##
InstallMethod( CycleStructureClass,
    "for a class function, and a class position",
    [ IsClassFunction, IsPosInt ],
    function( permchar, class )
    return CycleStructureClass( UnderlyingCharacterTable( permchar ),
                                ValuesOfClassFunction( permchar ), class );
    end );

InstallMethod( CycleStructureClass,
    "for an ordinary table, a list, and a class position",
    [ IsOrdinaryTable, IsHomogeneousList, IsPosInt ],
    function( tbl, permchar, class )
    local n,           # element order of `class'
          divs,        # divisors of `n'
          i, d, j,     # loop over `divs'
          fixed,       # numbers of fixed points
          cycstruct;   # cycle structure, result

    # Compute the numbers of fixed points of powers.
    n:= OrdersClassRepresentatives( tbl )[ class ];
    divs:= DivisorsInt( n );
    fixed:= [];
    for i in [ 1 .. Length( divs ) ] do

      # Compute the number of cycles of the element of order `n / d'.
      d:= divs[i];
      fixed[d]:= permchar[ PowerMap( tbl, d, class ) ];
      for j in [ 1 .. i-1 ] do
        if d mod divs[j] = 0 then

          # Subtract the number of fixed points with stabilizer exactly
          # of order `n / divs[j]'.
          fixed[d]:= fixed[d] - fixed[ divs[j] ];

        fi;
      od;

    od;

    # Convert these numbers into numbers of cycles.
    cycstruct:= [];
    for i in divs do
      if fixed[i] <> 0 and 1 < i then
--> --------------------

--> maximum size reached

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

[ Dauer der Verarbeitung: 0.62 Sekunden  (vorverarbeitet)  ]