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 84 kB image not shown  

Quelle  ctblmono.gi   Sprache: unbekannt

 
#############################################################################
##
##  This file is part of GAP, a system for computational discrete algebra.
##  This file's authors include Thomas Breuer, Erzsébet Horváth.
##
##  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 the functions dealing with monomiality questions for
##  solvable groups.
##
##  1. Character Degrees and Derived Length
##  2. Primitivity of Characters
##  3. Testing Monomiality
##  4. Minimal Nonmonomial Groups
##


#############################################################################
##
##  1. Character Degrees and Derived Length
##


#############################################################################
##
#M  Alpha( <G> )  . . . . . . . . . . . . . . . . . . . . . . . . for a group
##
InstallMethod( Alpha,
    "for a group",
    [ IsGroup ],
    function( G )

    local irr,        # irreducible characters of `G'
          degrees,    # set of degrees of `irr'
          chars,      # at position <i> all in `irr' of degree `degrees[<i>]'
          chi,        # one character
          alpha,      # result list
          max,        # maximal derived length found up to now
          kernels,    # at position <i> the kernels of all in `chars[<i>]'
          minimal,    # list of minimal kernels
          relevant,   # minimal kernels of one degree
          k,          # one kernel
          ker,
          dl;         # list of derived lengths

    Info( InfoMonomial, 1, "Alpha called for group ", G );

    # Compute the irreducible characters and the set of their degrees;
    # we need all irreducibles so it is reasonable to compute the table.
    irr:= List( Irr( G ), ValuesOfClassFunction );
    degrees:= Set( irr, x -> x[1] );
    RemoveSet( degrees, 1 );

    # Distribute characters to degrees.
    chars:= List( degrees, x -> [] );
    for chi in irr do
      if chi[1] > 1 then
        Add( chars[ Position( degrees, chi[1], 0 ) ], chi );
      fi;
    od;

    # Initialize
    alpha:= [ 1 ];
    max:= 1;

    # Compute kernels (as position lists)
    kernels:= List( chars, x -> Set( x, ClassPositionsOfKernel ) );

    # list of all minimal elements found up to now
    minimal:= [];

    Info( InfoMonomial, 1,
          "Alpha: There are ", Length( degrees )+1, " different degrees." );

    for ker in kernels do

      # We may remove kernels that contain a (minimal) kernel
      # of a character of smaller or equal degree.

      # Make sure to consider minimal elements of the actual degree first.
      SortBy( ker, Length );

      relevant:= [];

      for k in ker do
        if ForAll( minimal, x -> not IsSubsetSet( k, x ) ) then

          # new minimal element found
          Add( relevant, k );
          Add( minimal,  k );

        fi;
      od;

      # Give the trivial kernel a chance to be found first when we
      # consider the next larger degree.
      SortBy( minimal, Length );

      # Compute the derived lengths
      for k in relevant do

        dl:= DerivedLength( FactorGroupNormalSubgroupClasses(
                         OrdinaryCharacterTable( G ), k ) );
        if dl > max then
          max:= dl;
        fi;

      od;

      Add( alpha, max );

    od;

    Info( InfoMonomial, 1, "Alpha returns ", alpha );
    return alpha;
    end );


#############################################################################
##
#M  Delta( <G> )  . . . . . . . . . . . . . . . . . . . . . . . . for a group
##
InstallMethod( Delta,
    "for a group",
    [ IsGroup ],
    function( G )

    local delta,  # result list
          alpha,  # `Alpha( <G> )'
          r;      # loop variable

    delta:= [ 1 ];
    alpha:= Alpha( G );
    for r in [ 2 .. Length( alpha ) ] do
      delta[r]:= alpha[r] - alpha[r-1];
    od;

    return delta;
    end );


#############################################################################
##
#M  IsBergerCondition( <chi> )  . . . . . . . . . . . . . . . for a character
##
InstallMethod( IsBergerCondition,
    "for a class function",
    [ IsClassFunction ],
    function( chi )

    local tbl,         # character table of <chi>
          values,      # values of `chi'
          ker,         # intersection of kernels of smaller degree
          deg,         # degree of <chi>
          psi,         # one irreducible character of $G$
          kerchi,      # kernel of <chi> (as group)
          isberger;    # result

    Info( InfoMonomial, 1,
          "IsBergerCondition called for character ",
          CharacterString( chi, "chi" ) );

    values:= ValuesOfClassFunction( chi );
    deg:= values[1];
    tbl:= UnderlyingCharacterTable( chi );

    if 1 < deg then

      # We need all characters of smaller degree,
      # so it is reasonable to compute the character table of the group
      ker:= [ 1 .. Length( values ) ];
      for psi in Irr( UnderlyingCharacterTable( chi ) ) do
        if DegreeOfCharacter( psi ) < deg then
          IntersectSet( ker, ClassPositionsOfKernel( psi ) );
        fi;
      od;

      # Check whether the derived group of this normal subgroup
      # lies in the kernel of `chi'.
      kerchi:= ClassPositionsOfKernel( values );
      if IsSubsetSet( kerchi, ker ) then

        # no need to compute subgroups
        isberger:= true;
      else
        isberger:= IsSubset( KernelOfCharacter( chi ),
                     DerivedSubgroup( NormalSubgroupClasses( tbl, ker ) ) );
      fi;

    else
      isberger:= true;
    fi;

    Info( InfoMonomial, 1, "IsBergerCondition returns ", isberger );
    return isberger;
    end );


#############################################################################
##
#M  IsBergerCondition( <G> )  . . . . . . . . . . . . . . . . . . for a group
##
InstallMethod( IsBergerCondition,
    "for a group",
    [ IsGroup ],
    function( G )

    local tbl,         # character table of `G'
          psi,         # one irreducible character of `G'
          isberger,    # result
          degrees,     # different character degrees of `G'
          kernels,     #
          pos,         #
          i,           # loop variable
          leftinters,  #
          left,        #
          right;       #

    Info( InfoMonomial, 1, "IsBergerCondition called for group ", G );

    tbl:= OrdinaryCharacterTable( G );

    if Size( G ) mod 2 = 1 then

      isberger:= true;

    else

      # Compute the intersections of kernels of characters of same degree
      degrees:= [];
      kernels:= [];
      for psi in List( Irr( G ), ValuesOfClassFunction ) do
        pos:= Position( degrees, psi[1], 0 );
        if pos = fail then
          Add( degrees, psi[1] );
          Add( kernels, ShallowCopy( ClassPositionsOfKernel( psi ) ) );
        else
          IntersectSet( kernels[ pos ], ClassPositionsOfKernel( psi ) );
        fi;
      od;
      SortParallel( degrees, kernels );

      # Let $1 = f_1 \leq f_2 \leq\ldots \leq f_n$ the distinct
      # irreducible degrees of `G'.
      # We must have for all $1 \leq i \leq n-1$ that
      # $$
      #    ( \bigcap_{\psi(1) \leq f_i}  \ker(\psi) )^{\prime} \leq
      #      \bigcap_{\chi(1) = f_{i+1}} \ker(\chi)
      # $$

      i:= 1;
      isberger:= true;
      leftinters:= kernels[1];

      while i < Length( degrees ) and isberger do

        # `leftinters' becomes $\bigcap_{\psi(1) \leq f_i} \ker(\psi)$.
        IntersectSet( leftinters, kernels[i] );
        if not IsSubsetSet( kernels[i+1], leftinters ) then

          # we have to compute the groups
          left:= DerivedSubgroup( NormalSubgroupClasses( tbl, leftinters ) );
          right:= NormalSubgroupClasses( tbl, kernels[i+1] );
          if not IsSubset( right, left ) then
            isberger:= false;
            Info( InfoMonomial, 1,
                  "IsBergerCondition:  violated for character of degree ",
                  degrees[i+1] );
          fi;

        fi;
        i:= i+1;
      od;

    fi;

    Info( InfoMonomial, 1, "IsBergerCondition returns ", isberger );
    return isberger;
    end );


#############################################################################
##
##  2. Primitivity of Characters
##


#############################################################################
##
#F  TestHomogeneous( <chi>, <N> )
##
##  This works also for reducible <chi>.
##
InstallGlobalFunction( TestHomogeneous, function( chi, N )

    local t,        # character table of `G'
          classes,  # class lengths of `t'
          values,   # values of <chi>
          cl,       # classes of `G' that form <N>
          norm,     # norm of the restriction of <chi> to <N>
          tn,       # table of <N>
          fus,      # fusion of conjugacy classes <N> in $G$
          rest,     # restriction of <chi> to <N>
          i,        # loop over characters of <N>
          scpr;     # one scalar product in <N>

    values:= ValuesOfClassFunction( chi );

    if IsList( N ) then
      cl:= N;
    else
      cl:= ClassPositionsOfNormalSubgroup( UnderlyingCharacterTable( chi ),
                                           N );
    fi;

    if Length( cl ) = 1 then
      return rec( isHomogeneous := true,
                  comment       := "restriction to trivial subgroup" );
    fi;

    t:= UnderlyingCharacterTable( chi );
    classes:= SizesConjugacyClasses( t );
    norm:= Sum( cl, c -> classes[c] * values[c]
                                    * GaloisCyc( values[c], -1 ), 0 );

    if norm = Sum( classes{ cl }, 0 ) then

      # The restriction is irreducible.
      return rec( isHomogeneous := true,
                  comment       := "restricts irreducibly" );

    else

      # `chi' restricts reducibly.
      # Compute the table of `N' if necessary,
      # and check the constituents of the restriction
      N:= NormalSubgroupClasses( t, cl );
      tn:= CharacterTable( N );
      fus:= FusionConjugacyClasses( tn, t );
      rest:= values{ fus };

      for i in Irr( tn ) do
        scpr:= ScalarProduct( tn, ValuesOfClassFunction( i ), rest );
        if scpr <> 0 then

          # Return info about the constituent.
          return rec( isHomogeneous := ( scpr * DegreeOfCharacter( i )
                                         = values[1] ),
                      comment       := "restriction checked",
                      character     := i,
                      multiplicity  := scpr  );

        fi;
      od;

    fi;
end );


#############################################################################
##
#M  TestQuasiPrimitive( <chi> ) . . . . . . . . . . . . . . . for a character
##
##  This works also for reducible <chi>.
##  Note that a representation affording <chi> maps the centre of <chi>
##  to scalar matrices.
##
InstallMethod( TestQuasiPrimitive,
    "for a character",
    [ IsCharacter ],
    function( chi )

    local values,   # list of character values
          t,        # character table of `chi'
          nsg,      # list of normal subgroups of `t'
          cen,      # centre of `chi'
          j,        # loop over normal subgroups
          testhom,  # test of homogeneous restriction
          test;     # result record

    Info( InfoMonomial, 1,
          "TestQuasiPrimitive called for character ",
          CharacterString( chi, "chi" ) );

    values:= ValuesOfClassFunction( chi );

    # Linear characters are primitive.
    if values[1] = 1 then
      test:= rec( isQuasiPrimitive := true,
                  comment          := "linear character" );
    else

      t:= UnderlyingCharacterTable( chi );

      # Compute the normal subgroups of `G' containing the centre of `chi'.

      # Note that `chi' restricts homogeneously to all normal subgroups
      # of `G' if (and only if) it restricts homogeneously to all those
      # normal subgroups containing the centre of `chi'.

      # {\em Proof:}
      # Let $N \unlhd G$ such that $Z(\chi) \not\leq N$.
      # We have to show that $\chi$ restricts homogeneously to $N$.
      # By our assumption $\chi_{N Z(\chi)}$ is homogeneous,
      # take $\vartheta$ the irreducible constituent.
      # Let $D$ a representation affording $\vartheta$ such that
      # the restriction to $N$ consists of block diagonal matrices
      # corresponding to the irreducible constituents.
      # $D( Z(\chi) )$ consists of scalar matrices,
      # thus $D( n^x ) = D( n )$ for $n\in N$, $x\in Z(\chi)$,
      # i.e., $Z(\chi)$ acts trivially on the irreducible constituents
      # of $\vartheta_N$,
      # i.e., every constituent of $\vartheta_N$ is invariant in $N Z(\chi)$,
      # i.e., $\vartheta$ (and thus $\chi$) restricts homogeneously to $N$.

      cen:= ClassPositionsOfCentre( values );
      nsg:= ClassPositionsOfNormalSubgroups( t );
      nsg:= Filtered( nsg, x -> IsSubsetSet( x, cen ) );

      test:= rec( isQuasiPrimitive := true,
                  comment          := "all restrictions checked" );

      for j in nsg do
        testhom:= TestHomogeneous( chi, j );
        if not testhom.isHomogeneous then

          # nonhomogeneous restriction found
          test:= rec( isQuasiPrimitive := false,
                      comment          := testhom.comment,
                      character        := testhom.character );
          break;
        fi;
      od;

    fi;

    Info( InfoMonomial, 1,
          "TestQuasiPrimitive returns `", test.isQuasiPrimitive, "'" );

    return test;
    end );


#############################################################################
##
#M  IsQuasiPrimitive( <chi> ) . . . . . . . . . . . . . . . . for a character
##
InstallMethod( IsQuasiPrimitive,
    "for a character",
    [ IsCharacter ],
    chi -> TestQuasiPrimitive( chi ).isQuasiPrimitive );


#############################################################################
##
#M  IsPrimitiveCharacter( <chi> ) . . . . . . . . . . . . . . for a character
##
##  Quasi-primitive irreducible characters of solvable groups are primitive,
##  see for example [Isa76, Thm. 11.33].
##
InstallMethod( IsPrimitiveCharacter,
    "for a class function",
    [ IsClassFunction ],
    function( chi )
    if not ( IsIrreducibleCharacter( chi ) and
             IsSolvableGroup( UnderlyingGroup( chi ) ) ) then
      TryNextMethod();
    fi;
    return TestQuasiPrimitive( chi ).isQuasiPrimitive;
    end );


#############################################################################
##
#M  IsPrimitive( <chi> )  . . . . . . . . . . . . . . . . . . for a character
##
InstallOtherMethod( IsPrimitive,
    "for a character",
    [ IsClassFunction ],
    IsPrimitiveCharacter );
#T really install this?


#############################################################################
##
#F  TestInducedFromNormalSubgroup( <chi>[, <N>] )
##
InstallGlobalFunction( TestInducedFromNormalSubgroup, function( arg )

    local sizeN,      # size of <N>
          sizefactor, # size of $G / <N>$
          values,     # values list of `chi'
          m,          # list of all maximal normal subgroups of $G$
          test,       # intermediate result
          tn,         # character table of <N>
          irr,        # irreducibles of `tn'
          i,          # loop variable
          scpr,       # one scalar product in <N>
          N,          # optional second argument
          cl,         # classes corresponding to `N'
          chi;        # first argument

    # check the arguments
    if Length( arg ) < 1 or Length( arg ) > 2
       or not IsCharacter( arg[1] ) then
      Error( "usage: TestInducedFromNormalSubgroup( <chi>[, <N>] )" );
    fi;

    chi:= arg[1];

    Info( InfoMonomial, 1,
          "TestInducedFromNormalSubgroup called with character ",
          CharacterString( chi, "chi" ) );

    if Length( arg ) = 1 then

      # `TestInducedFromNormalSubgroup( <chi> )'
      if DegreeOfCharacter( chi ) = 1 then

        return rec( isInduced:= false,
                    comment  := "linear character" );

      else

        # Get all maximal normal subgroups.
        m:= ClassPositionsOfMaximalNormalSubgroups(
                UnderlyingCharacterTable( chi ) );

        for N in m do

          test:= TestInducedFromNormalSubgroup( chi, N );
          if test.isInduced then
            return test;
          fi;

        od;

        return rec( isInduced := false,
                    comment   := "all maximal normal subgroups checked" );
      fi;

    else

      # `TestInducedFromNormalSubgroup( <chi>, <N> )'

      N:= arg[2];

      # 1. If the degree of <chi> is not divisible by the index of <N> in $G$
      #    then <chi> cannot be induced from <N>.
      # 2. If <chi> does not vanish outside <N> it cannot be induced from
      #    <N>.
      # 3. Provided that <chi> vanishes outside <N>,
      #    <chi> is induced from <N> if and only if the restriction of <chi>
      #    to <N> has an irreducible constituent with multiplicity 1.
      #
      #    Since the scalar product of the restriction with itself has value
      #    $G \: N$, multiplicity 1 means that there are $G \: N$ conjugates
      #    of this constituent, so <chi> is induced from each of them.
      #
      #    This gives another necessary condition that is easy to check.
      #    Namely, <N> must have more than $G \: <N>$ conjugacy classes if
      #    <chi> is induced from <N>.

      if IsList( N ) then
        sizeN:= Sum( SizesConjugacyClasses(
                         UnderlyingCharacterTable( chi ) ){ N }, 0 );
      elif IsGroup( N ) then
        sizeN:= Size( N );
      else
        Error( "<N> must be a group or a list" );
      fi;

      sizefactor:= Size( UnderlyingCharacterTable( chi ) ) / sizeN;

      if   DegreeOfCharacter( chi ) mod sizefactor <> 0 then

        return rec( isInduced := false,
                    comment   := "degree not divisible by index" );

      elif sizeN <= sizefactor then

        return rec( isInduced := false,
                    comment   := "<N> has too few conjugacy classes" );

      fi;

      values:= ValuesOfClassFunction( chi );

      if IsList( N ) then

        # Check whether the character vanishes outside <N>.
        for i in [ 2 .. Length( values ) ] do
          if not i in N and values[i] <> 0 then
            return rec( isInduced := false,
                        comment   := "<chi> does not vanish outside <N>" );
          fi;
        od;

        cl:= N;
        N:= NormalSubgroupClasses( UnderlyingCharacterTable( chi ), N );

      else

        # Check whether <N> has less conjugacy classes than its index is.
        if Length( ConjugacyClasses( N ) ) <= sizefactor then

          return rec( isInduced := false,
                      comment   := "<N> has too few conjugacy classes" );

        fi;

        cl:= ClassPositionsOfNormalSubgroup( UnderlyingCharacterTable( chi ),
                                             N );

        # Check whether the character vanishes outside <N>.
        for i in [ 2 .. Length( values ) ] do
          if not i in cl and values[i] <> 0 then
            return rec( isInduced := false,
                        comment   := "<chi> does not vanish outside <N>" );
          fi;
        od;

      fi;

      # Compute the restriction to <N>.
      chi:= values{ FusionConjugacyClasses( OrdinaryCharacterTable( N ),
                        UnderlyingCharacterTable( chi ) ) };

      # Check possible constituents.
      tn:= CharacterTable( N );
      irr:= Irr( N );
      for i in [ 1 .. NrConjugacyClasses( tn ) - sizefactor + 1 ] do

        scpr:= ScalarProduct( tn, ValuesOfClassFunction( irr[i] ), chi );

        if   1 < scpr then

          return rec( isInduced := false,
                      comment   := Concatenation(
                                     "constituent with multiplicity ",
                                     String( scpr ) ) );

        elif scpr = 1 then

          return rec( isInduced := true,
                      comment   := "induced from component \'.character\'",
                      character := irr[i] );

        fi;

      od;

      return rec( isInduced := false,
                  comment   := "all irreducibles of <N> checked" );

    fi;
end );


#############################################################################
##
#M  IsInducedFromNormalSubgroup( <chi> )  . . . . . . . . . . for a character
##
InstallMethod( IsInducedFromNormalSubgroup,
    "for a character",
    [ IsCharacter ],
    chi -> TestInducedFromNormalSubgroup( chi ).isInduced );


#############################################################################
##
##  3. Testing Monomiality
##


#############################################################################
##
#M  TestSubnormallyMonomial( <G> )  . . . . . . . . . . . . . . . for a group
##
InstallMethod( TestSubnormallyMonomial,
    "for a group",
    [ IsGroup ],
    function( G )

    local test,       # result record
          orbits,     # orbits of characters
          chi,        # loop over `orbits'
          found,      # decision is found
          i;          # loop variable

    Info( InfoMonomial, 1,
          "TestSubnormallyMonomial called for group ",
          GroupString( G, "G" ) );

    if IsNilpotentGroup( G ) then

      # Nilpotent groups are subnormally monomial.
      test:= rec( isSubnormallyMonomial:= true,
                  comment := "nilpotent group" );

    else

      # Check SM character by character,
      # one representative of each orbit under Galois conjugacy
      # and multiplication with linear characters only.

      orbits:= OrbitRepresentativesCharacters( Irr( G ) );

      # For each representative check whether it is SM.
      # (omit linear characters, i.e., first position)
      found:= false;
      i:= 2;
      while ( not found ) and i <= Length( orbits ) do

        chi:= orbits[i];
        if not TestSubnormallyMonomial( chi ).isSubnormallyMonomial then

          found:= true;
          test:= rec( isSubnormallyMonomial := false,
                      character             := chi,
                      comment               := "found non-SM character" );

        fi;
        i:= i+1;

      od;

      if not found then

        test:= rec( isSubnormallyMonomial := true,
                    comment               := "all irreducibles checked" );

      fi;

    fi;

    # Return the result.
    Info( InfoMonomial, 1,
          "TestSubnormallyMonomial returns with `",
          test.isSubnormallyMonomial, "'" );
    return test;
    end );


#############################################################################
##
#M  TestSubnormallyMonomial( <chi> )  . . . . . . . . . . . . for a character
##
InstallMethod( TestSubnormallyMonomial,
    "for a character",
    [ IsClassFunction ],
    function( chi )

    local test,       # result record
          testsm;     # local function for recursive check

    Info( InfoMonomial, 1,
          "TestSubnormallyMonomial called for character ",
          CharacterString( chi, "chi" ) );

    if   DegreeOfCharacter( chi ) = 1 then

      # Linear characters are subnormally monomial.
      test:= rec( isSubnormallyMonomial := true,
                  comment               := "linear character",
                  character             := chi );

    elif     HasIsSubnormallyMonomial( UnderlyingGroup( chi ) )
         and IsSubnormallyMonomial( UnderlyingGroup( chi ) ) then

      # If the group knows that it is subnormally monomial return this.
      test:= rec( isSubnormallyMonomial := true,
                  comment               := "subnormally monomial group",
                  character             := chi );

    elif IsNilpotentGroup( UnderlyingGroup( chi ) ) then

      # Nilpotent groups are subnormally monomial.
      test:= rec( isSubnormallyMonomial := true,
                  comment               := "nilpotent group",
                  character             := chi );

    else

      # We have to check recursively.

      # Given a character `chi' of the group $N$, and two classes lists
      # `forbidden' and `allowed' that describe all maximal normal
      # subgroups of $N$, where `forbidden' denotes all those normal
      # subgroups through that `chi' cannot be subnormally induced,
      # return either a linear character of a subnormal subgroup of $N$
      # from that `chi' is induced, or `false' if no such character exists.
      # If we reach a nilpotent group then we return a character of this
      # group, so the character is not necessarily linear.

      testsm:= function( chi, forbidden, allowed )

      local N,       # group of `chi'
            mns,     # max. normal subgroups
            forbid,  #
            n,       # one maximal normal subgroup
            cl,
            len,
            nt,
            fus,
            rest,
            deg,
            const,
            nallowed,
            nforbid,
            gp,
            fusgp,
            test;

      forbid:= ShallowCopy( forbidden );
      N:= UnderlyingGroup( chi );
      chi:= ValuesOfClassFunction( chi );
      len:= Length( chi );

      # Loop over `allowed'.
      for cl in allowed do

        if ForAll( [ 1 .. len ], x -> chi[x] = 0 or x in cl ) then

          # `chi' vanishes outside `n', so is induced from `n'.

          n:= NormalSubgroupClasses( OrdinaryCharacterTable( N ), cl );
          nt:= CharacterTable( n );

          # Compute a constituent of the restriction of `chi' to `n'.
          fus:= FusionConjugacyClasses( nt, OrdinaryCharacterTable( N ) );
          rest:= chi{ fus };
          deg:= chi[1] * Size( n ) / Size( N );
          const:= First( Irr( n ),
                     x ->     DegreeOfCharacter( x ) = deg
                          and ScalarProduct( nt, ValuesOfClassFunction( x ),
                                                 rest ) <> 0 );

          # Check termination.
          if   deg = 1 or IsNilpotentGroup( n ) then
            return const;
          elif Length( allowed ) = 0 then
            return false;
          fi;

          # Compute allowed and forbidden maximal normal subgroups of `n'.
          mns:= ClassPositionsOfMaximalNormalSubgroups( nt );
          nallowed:= [];
          nforbid:= [];
          for gp in mns do

            # A group is forbidden if it is the intersection of a group
            # in `forbid' with `n'.
            fusgp:= Set( fus{ gp } );
            if ForAny( forbid, x -> IsSubsetSet( x, fusgp ) ) then
              Add( nforbid, gp );
            else
              Add( nallowed, gp );
            fi;

          od;

          # Check whether `const' is subnormally induced from `n'.
          test:= testsm( const, nforbid, nallowed );
          if test <> false then
            return test;
          fi;

        fi;

        # Add `n' to the forbidden subgroups.
        Add( forbid, cl );

      od;

      # All allowed normal subgroups have been checked.
      return false;
      end;


      # Run the recursive search.
      # Here all maximal normal subgroups are allowed.
      test:= testsm( chi, [], ClassPositionsOfMaximalNormalSubgroups(
                                  UnderlyingCharacterTable( chi ) ) );

      # Prepare the output.
      if test = false then
        test:= rec( isSubnormallyMonomial := false,
                    comment   := "all subnormal subgroups checked" );
      elif DegreeOfCharacter( test ) = 1 then
        test:= rec( isSubnormallyMonomial := true,
                    comment   := "reduced to linear character",
                    character := test );
      else
        test:= rec( isSubnormallyMonomial := true,
                    comment   := "reduced to nilpotent subgroup",
                    character := test );
      fi;

    fi;

    Info( InfoMonomial, 1,
          "TestSubnormallyMonomial returns with `",
          test.isSubnormallyMonomial, "'" );
    return test;
    end );


#############################################################################
##
#M  IsSubnormallyMonomial( <G> )  . . . . . . . . . . . . . . . . for a group
#M  IsSubnormallyMonomial( <chi> )  . . . . . . . . . . . . . for a character
##
InstallMethod( IsSubnormallyMonomial,
    "for a group",
    [ IsGroup ],
    G -> TestSubnormallyMonomial( G ).isSubnormallyMonomial );

InstallMethod( IsSubnormallyMonomial,
    "for a character",
    [ IsClassFunction ],
    chi -> TestSubnormallyMonomial( chi ).isSubnormallyMonomial );


#############################################################################
##
#M  IsMonomialNumber( <n> ) . . . . . . . . . . . . .  for a positive integer
##
InstallMethod( IsMonomialNumber,
    "for a positive integer",
    [ IsPosInt ],
    function( n )

    local factors,   # list of prime factors of `n'
          collect,   # list of (prime divisor, exponent) pairs
          nu2,       # $\nu_2(n)$
          pair,      # loop over `collect'
          pair2,     # loop over `collect'
          ord;       # multiplicative order

    factors := Factors(Integers, n );
    collect := Collected( factors );

    # Get $\nu_2(n)$.
    if 2 in factors then
      nu2:= collect[1][2];
    else
      nu2:= 0;
    fi;

    # Check for minimal nonmonomial groups of type 1.
    if nu2 >= 2 then
      for pair in collect do
        if pair[1] mod 4 = 3 and pair[2] >= 3 then
          return false;
        fi;
      od;
    fi;

    # Check for minimal nonmonomial groups of type 2.
    if nu2 >= 3 then
      for pair in collect do
        if pair[1] mod 4 = 1 and pair[2] >= 3 then
          return false;
        fi;
      od;
    fi;

    # Check for minimal nonmonomial groups of type 3.
    for pair in collect do
      for pair2 in collect do
        if pair[1] <> pair2[1] and pair2[1] <> 2 then
          ord:= OrderMod( pair[1], pair2[1] );
          if ord mod 2 = 0 and ord < pair[2] then
            return false;
          fi;
        fi;
      od;
    od;

    # Check for minimal nonmonomial groups of type 4.
    if nu2 >= 4 then
      for pair in collect do
        if pair[1] <> 2 and nu2 >= 2* OrderMod( 2, pair[1] ) + 2 then
          return false;
        fi;
      od;
    fi;

    # Check for minimal nonmonomial groups of type 5.
    if nu2 >= 2 then
      for pair in collect do
        if pair[1] mod 4 = 1 and pair[2] >= 3 then
          for pair2 in collect do
            if pair2[1] <> 2 then
              ord:= OrderMod( pair[1], pair2[1] );
              if ord mod 2 = 1 and 2 * ord < pair[2] then
                return false;
              fi;
            fi;
          od;
        fi;
      od;
    fi;

    # None of the five cases can occur.
    return true;
    end );


#############################################################################
##
#M  TestMonomialQuick( <chi> )  . . . . . . . . . . . . . . . for a character
##
##  We assume that <chi> is an irreducible character.
##
InstallMethod( TestMonomialQuick,
    "for a character",
    [ IsClassFunction ],
    function( chi )

    local G,          # group of `chi'
          factsize,   # size of the kernel factor of `chi'
          codegree,   # codegree of `chi'
          pi,         # prime divisors of a Hall subgroup
          hall,       # size of `pi' Hall subgroup of kernel factor
          ker,        # kernel of `chi'
          t,          # character table of `G'
          grouptest;  # result of the call to `G / ker'

    Info( InfoMonomial, 1,
          "TestMonomialQuick called for character ",
          CharacterString( chi, "chi" ) );

    if   HasIsMonomialCharacter( chi ) then

      # The character knows about being monomial.
      Info( InfoMonomial, 1,
            "TestMonomialQuick returns with `",
            IsMonomialCharacter( chi ), "'" );
      return rec( isMonomial := IsMonomialCharacter( chi ),
                  comment    := "was already stored" );

    elif DegreeOfCharacter( chi ) = 1 then

      # Linear characters are monomial.
      Info( InfoMonomial, 1,
            "TestMonomialQuick returns with `true'" );
      return rec( isMonomial := true,
                  comment    := "linear character" );

    fi;

    G:= UnderlyingGroup( chi );

    if Size( G ) mod DegreeOfCharacter( chi ) <> 0 then
      Info( InfoMonomial, 1,
            "TestMonomialQuick returns with `false'" );
      return rec( isMonomial := false,
                  comment    := "degree does not divide group order" );
    fi;

    # The following criteria are applicable only to irreducible characters.
    # We do *not* check here that 'chi' is really an irreducible character.
    if TestMonomialQuick( G ).isMonomial = true then

      # The whole group is known to be monomial.
      Info( InfoMonomial, 1,
            "TestMonomialQuick returns with `true'" );
      return rec( isMonomial := true,
                  comment    := "whole group is monomial" );

    fi;

    chi := ValuesOfClassFunction( chi );

    # Replace `G' by the factor group modulo the kernel.
    ker:= ClassPositionsOfKernel( chi );
    if 1 < Length( ker ) then
      t:= CharacterTable( G );
      factsize:= Size( G ) / Sum( SizesConjugacyClasses( t ){ ker }, 0 );
    else
      factsize:= Size( G );
    fi;

    # Inspect the codegree.
    codegree := factsize / chi[1];
    if IsPrimePowerInt( codegree ) then

      # If the codegree is a prime power then the character is monomial,
      # by a result of Chillag, Mann, and Manz.
      # Here is a short proof due to M. I. Isaacs
      # (communicated by E. Horváth).
      #
      # Let $G$ be a finite group, $\chi\in Irr(G)$ with codegree $p^a$
      # for a prime $p$, and $P\in Syl_p(G)$.
      # Then there exists an irreducible character $\psi$ of $P$
      # with $\psi^G = \chi$.
      #
      # {\it Proof:}
      # Let $b$ be an integer such that $\chi(1) = [G : P] p^b$,
      # and consider $\chi_P = \sum_{\psi\in Irr(P)} a_{\psi} \psi$.
      # There exists $\psi$ with $a_{\psi} \not= 0$ and $\psi(1) \leq p^b$,
      # as otherwise $\chi(1)$ would be divisible by a larger power of $p$.
      # On the other hand, $\chi$ must be a constituent of $\psi^G$ and thus
      # $p^b \leq \psi(1)$.
      # So there is equality, and thus $\psi^G = \chi$.
      Info( InfoMonomial, 1,
            "TestMonomialQuick returns with `true'" );
      return rec( isMonomial := true,
                  comment    := "codegree is prime power" );
    fi;

    # If $G$ is solvable and $\pi$ is the set of primes dividing the codegree
    # then the character is induced from a $\pi$ Hall subgroup.
    # This follows from Theorem~(2D) in~\cite{Fon62}.
    if IsSolvableGroup( G ) then

      pi   := PrimeDivisors( codegree );
      hall := Product( Filtered( Factors(Integers, factsize ), x -> x in pi ), 1 );

      if factsize / hall = chi[1] then

        # The character is induced from a *linear* character
        # of the $\pi$ Hall group.
        Info( InfoMonomial, 1,
              "TestMonomialQuick returns with `true'" );
        return rec( isMonomial := true,
                    comment    := "degree is index of Hall subgroup" );

      elif IsMonomialNumber( hall ) then

        # The *order* of this Hall subgroup is monomial.
        Info( InfoMonomial, 1,
              "TestMonomialQuick returns with `true'" );
        return rec( isMonomial := true,
                    comment    := "induced from monomial Hall subgroup" );

      fi;

    fi;

    # Inspect the factor group modulo the kernel.
    if 1 < Length( ker ) then

      # For solvable 'G', checking 'factsize' for monomiality does not
      # help here because the divisor 'hall' has been checked above.

      if IsSubsetSet( ker, ClassPositionsOfSupersolvableResiduum(t) ) then

        # The factor group modulo the kernel is supersolvable.
        Info( InfoMonomial, 1,
              "TestMonomialQuick returns with `true'" );
        return rec( isMonomial:= true,
                    comment:= "kernel factor group is supersolvable" );

      fi;

      grouptest:= TestMonomialQuick( FactorGroupNormalSubgroupClasses(
                      OrdinaryCharacterTable( G ), ker ) );
#T This is not cheap!
      if grouptest.isMonomial = true then

        Info( InfoMonomial, 1,
              "#I  TestMonomialQuick returns with `true'" );
        return rec( isMonomial := true,
                    comment    := "kernel factor group is monomial" );

      fi;

    fi;

    # No more cheap tests are available.
    Info( InfoMonomial, 1,
          "TestMonomialQuick returns with `?'" );
    return rec( isMonomial := "?",
                comment    := "no decision by cheap tests" );
    end );


##############################################################################
##
#M  TestMonomialQuick( <G> )  . . . . . . . . . . . . . . . . . .  for a group
##
##  The following criteria are used for a group <G>.
##
##  o Nonsolvable groups are not monomial.
##  o If the group order is monomial then <G> is monomial.
##    (Note that monomiality of group orders is defined for solvable
##     groups only, so solvability has to be checked first.)
##  o Nilpotent groups are monomial.
##  o Abelian by supersolvable groups are monomial.
##  o Sylow abelian by supersolvable groups are monomial.
##    (Compute the Sylow subgroups of the supersolvable residuum,
##     and check whether they are abelian.)
##
InstallMethod( TestMonomialQuick,
    "for a group",
    [ IsGroup ],
    function( G )

#T if the table is known then call TestMonomialQuick( G.charTable ) !
#T (and implement this function ...)

    local test,       # the result record
          ssr;        # supersolvable residuum of `G'

    Info( InfoMonomial, 1,
          "TestMonomialQuick called for group ",
          GroupString( G, "G" ) );

    # If the group knows about being monomial return this.
    if   HasIsMonomialGroup( G ) then

      test:= rec( isMonomial := IsMonomialGroup( G ),
                  comment    := "was already stored" );

    elif not IsSolvableGroup( G ) then

      # Monomial groups are solvable.
      test:= rec( isMonomial := false,
                  comment    := "non-solvable group" );

    elif IsMonomialNumber( Size( G ) ) then

      # Every solvable group of this order is monomial.
      test:= rec( isMonomial := true,
                  comment    := "group order is monomial" );

    elif IsNilpotentGroup( G ) then

      # Nilpotent groups are monomial.
      test:= rec( isMonomial := true,
                  comment    := "nilpotent group" );

    else

      ssr:= SupersolvableResiduum( G );

      if IsTrivial( ssr ) then

        # Supersolvable groups are monomial.
        test:= rec( isMonomial := true,
                    comment    := "supersolvable group" );

      elif IsAbelian( ssr ) then

        # Abelian by supersolvable groups are monomial.
        test:= rec( isMonomial := true,
                    comment    := "abelian by supersolvable group" );

      elif ForAll( PrimeDivisors( Size( ssr ) ),
                   x -> IsAbelian( SylowSubgroup( ssr, x ) ) ) then

        # Sylow abelian by supersolvable groups are monomial.
        test:= rec( isMonomial := true,
                    comment    := "Sylow abelian by supersolvable group" );

      else

        # No more cheap tests are available.
        test:= rec( isMonomial := "?",
                    comment    := "no decision by cheap tests" );

      fi;

    fi;

    Info( InfoMonomial, 1,
          "TestMonomialQuick returns with `", test.isMonomial, "'" );
    return test;
    end );


#############################################################################
##
#M  TestMonomial( <chi> ) . . . . . . . . . . . . . . . . . . for a character
#M  TestMonomial( <chi>, <uselattice> ) . . .  for a character, and a Boolean
##
##  Called with an irreducible character <chi> as argument,
##  `TestMonomialQuick( <chi> )' is inspected first;
##  if this did not decide the question,
##  we test all those normal subgroups of $G$ to which <chi> restricts
##  nonhomogeneously whether the interesting character of the
##  inertia subgroup is monomial.
##  (If <chi> is quasiprimitive then it is nonmonomial.)
##  If <chi> is not irreducible, these tests are not applicable.
##
##  If <uselattice> is `true' or if the group of <chi> has order at most
##  `TestMonomialUseLattice' then the subgroup lattice is used
##  to decide the question if necessary.
##
BindGlobal( "TestMonomialFromLattice", function( chi )
    local G, H, source;

    G:= UnderlyingGroup( chi );

    # Loop over representatives of the conjugacy classes of subgroups.
    for H in List( ConjugacyClassesSubgroups( G ), Representative ) do
      if IndexNC( G, H ) = chi[1] then
        source:= First( LinearCharacters( H ), lambda -> lambda^G = chi );
        if source <> fail then
          return source;
        fi;
      fi;
    od;

    # Return the negative result.
    return fail;
end );

InstallMethod( TestMonomial,
    "for a character",
    [ IsClassFunction ],
    chi -> TestMonomial( chi, false ) );

InstallMethod( TestMonomial,
    "for a character, and a Boolean",
    [ IsClassFunction, IsBool ],
    function( chi, uselattice )
    local G,         # group of `chi'
          test,      # result record
          t,         # character table of `G'
          nsg,       # list of normal subgroups of `G'
          ker,       # kernel of `chi'
          isqp,      # is `chi' quasiprimitive
          i,         # loop over normal subgroups
          testhom,   # does `chi' restrict homogeneously
          theta,     # constituent of the restriction
          found,     # monomial character found
          found2,    # monomial character found
          T,         # inertia group of `theta'
          fus,       # fusion of conjugacy classes `T' in `G'
          deg,       # degree of `theta'
          rest,      # restriction of `chi' to `T'
          j,         # loop over irreducibles of `T'
          psi,       # character of `T'
          testmon;   # test for monomiality

    Info( InfoMonomial, 1, "TestMonomial called" );

    # Start with elementary tests for monomiality.
    if   HasIsMonomialCharacter( chi ) then
      # The character knows about being monomial.
      test:= rec( isMonomial := IsMonomialCharacter( chi ),
                  comment    := "was already stored" );
    elif IsIrreducibleCharacter( chi ) then
      test:= TestMonomialQuick( chi );
    elif DegreeOfCharacter( chi ) = 1 then
      # Linear characters are monomial.
      test:= rec( isMonomial := true,
                  comment    := "linear character" );
    elif Size( UnderlyingGroup( chi ) ) mod DegreeOfCharacter( chi ) <> 0 then
      test:= rec( isMonomial := false,
                  comment    := "degree does not divide group order" );
    else
      test:= rec( isMonomial:= "?" );
    fi;

    if test.isMonomial = "?" then

      G:= UnderlyingGroup( chi );

      if not IsSolvableGroup( G ) then
        Info( InfoMonomial, 1,
              "TestMonomial: nonsolvable group" );
        test:= rec( isMonomial := "?",
                    comment    := "no criterion for nonsolvable group" );
      elif not IsIrreducibleCharacter( chi ) then
        Info( InfoMonomial, 1,
              "TestMonomial: reducible character" );
        test:= rec( isMonomial := "?",
                    comment    := "no criterion for reducible character" );
      else

        # Loop over all normal subgroups of `G' to that <chi> restricts
        # nonhomogeneously.
        # (If there are no such normal subgroups then <chi> is
        # quasiprimitive hence not monomial.)
        t:= CharacterTable( G );
        ker:= ClassPositionsOfKernel( ValuesOfClassFunction( chi ) );
        nsg:= Filtered( ClassPositionsOfNormalSubgroups( t ),
                        x -> IsSubsetSet( x, ker ) );
        isqp:= true;

        i:= 1;
        found:= false;

        while not found and i <= Length( nsg ) do

          testhom:= TestHomogeneous( chi, nsg[i] );
          if not testhom.isHomogeneous then

            isqp:= false;

            # Take a constituent `theta' in a nonhomogeneous restriction.
            theta:= testhom.character;

            # We have $<chi>_N = e \sum_{i=1}^t \theta_i$.
            # <chi> is induced from an irreducible character of
            # $'T' = I_G(\theta_1)$ that restricts to $e \theta_1$,
            # so we have proved monomiality if $e = \theta(1) = 1$.
            if     testhom.multiplicity = 1
               and DegreeOfCharacter( theta ) = 1 then

              found:= true;
              test:= rec( isMonomial := true,
                          comment    := "induced from \'character\'",
                          character  := theta );

            else

              # Compute the inertia group `T'.
              T:= InertiaSubgroup( G, theta );
              if TestMonomialQuick( T ).isMonomial = true then

                # `chi' is induced from `T', and `T' is monomial.
                found:= true;
                test:= rec( isMonomial := true,
                            comment    := "induced from monomial subgroup",
                            subgroup   := T );
#T example?

              else

                # Check whether a character of `T' from that <chi>
                # is induced can be proved to be monomial.

                # First get all characters `psi' of `T'
                # from that <chi> is induced.
                t:= Irr( T );
                fus:= FusionConjugacyClasses( OrdinaryCharacterTable( T ),
                                              OrdinaryCharacterTable( G ) );
                deg:= DegreeOfCharacter( chi ) / Index( G, T );
                rest:= ValuesOfClassFunction( chi ){ fus };
                j:= 1;
                found2:= false;
                while not found2 and j <= Length(t) do
                  if     DegreeOfCharacter( t[j] ) = deg
                     and ScalarProduct( CharacterTable( T ),
                                        ValuesOfClassFunction( t[j] ),
                                        rest ) <> 0 then
                    psi:= t[j];
                    testmon:= TestMonomial( psi );
                    if testmon.isMonomial = true then
                      found:= true;
                      found2:= true;
                      test:= testmon;
                    fi;
                  fi;
                  j:= j+1;
                od;

              fi;

            fi;

          fi;

          i:= i+1;

        od;

        if isqp then

          # <chi> is quasiprimitive, for a solvable group this implies
          # primitivity,
          # for a nonlinear character this proves nonmonomiality.
          test:= rec( isMonomial := false,
                      comment    := "quasiprimitive character" );

        elif not found then

          # We have tried all suitable normal subgroups and always got
          # back that the character of the inertia subgroup was
          # (possibly) nonmonomial.
          test:= rec( isMonomial:= "?",
                      comment:= "all inertia subgroups checked, no result" );

        fi;

      fi;

      if test.isMonomial = "?" and
         ( uselattice or Size( G ) <= TestMonomialUseLattice ) then
        # Use explicit computations with the subgroup lattice,
        test:= TestMonomialFromLattice( chi );
        if test = fail then
          test:= rec( isMonomial := false,
                      comment    := "lattice checked" );
        else
          test:= rec( isMonomial := true,
                      comment    := "induced from \'character\'",
                      character  := test );
        fi;
      fi;

    fi;

    # Return the result.
    Info( InfoMonomial, 1,
          "TestMonomial returns with `", test.isMonomial, "'" );
    return test;
    end );


#############################################################################
##
#M  TestMonomial( <G> ) . . . . . . . . . . . . . . . . . . . . . for a group
#M  TestMonomial( <G>, <uselattice> ) . . . . . .  for a group, and a Boolean
##
##  Called with a group <G>, the program checks whether all representatives
##  of character orbits are monomial.
##
InstallMethod( TestMonomial,
    "for a group",
    [ IsGroup ],
    G -> TestMonomial( G, false ) );

InstallMethod( TestMonomial,
    "for a group, and a Boolean",
    [ IsGroup, IsBool ],
    function( G, uselattice )

    local test,      # result record
          found,     # monomial character found
          testmon,   # test for monomiality
          j,         # loop over irreducibles of `T'
          psi,       # character of `T'
          orbits,    # orbits of irreducibles of `T'
          poss;      # list of possibly nonmonomial characters

    Info( InfoMonomial, 1, "TestMonomial called for a group" );

    # elementary test for monomiality
    test:= TestMonomialQuick( G );

    if test.isMonomial = "?" then

      if Size( G ) mod 2 = 0 and ForAny( Delta( G ), x -> 1 < x ) then

        # For even order groups it is checked whether
        # the list `Delta( G )' contains an entry that is bigger
        # than one. (For monomial groups and for odd order groups
        # this is always less than one,
        # according to Taketa's Theorem and Berger's result).
        test:= rec( isMonomial := false,
                    comment    := "list Delta( G ) contains entry > 1" );

      else

        orbits:= OrbitRepresentativesCharacters( Irr( G ) );
        found:= false;
        j:= 2;
        poss:= [];
        while j <= Length( orbits ) do
          psi:= orbits[j];
          testmon:= TestMonomial( psi, uselattice ).isMonomial;
          if testmon = false then
            found:= true;
            break;
          elif testmon = "?" then
            Add( poss, psi );
          fi;
          j:= j+1;
        od;

        if found then

          # A nonmonomial character was found.
          test:= rec( isMonomial := false,
                      comment    := "nonmonomial character found",
                      character  := psi );

        elif IsEmpty( poss ) then

          # All checks answered `true'.
          test:= rec( isMonomial := true,
                      comment    := "all characters checked" );

        else

          # We give up.
          test:= rec( isMonomial := "?",
                      comment    := "(possibly) nonmon. characters found",
                      characters := poss );

        fi;

      fi;

    fi;

    # Return the result.
    Info( InfoMonomial, 1,
          "TestMonomial returns with `", test.isMonomial, "'" );
    return test;
    end );


#############################################################################
##
#M  IsMonomialGroup( <G> ) . . . . . . . . . . . . . . . . . . .  for a group
##
InstallMethod( IsMonomialGroup,
    "for a group",
    [ IsGroup ],
    G -> TestMonomial( G, true ).isMonomial );


#############################################################################
##
#M  IsMonomialCharacter( <chi> )  . . . . . . . . . . . . . . for a character
##
InstallMethod( IsMonomialCharacter,
    "for a character",
    [ IsClassFunction ],
    chi -> TestMonomial( chi, true ).isMonomial );


#############################################################################
##
#A  TestRelativelySM( <G> )
#A  TestRelativelySM( <chi> )
#F  TestRelativelySM( <G>, <N> )
#F  TestRelativelySM( <chi>, <N> )
##
##  The algorithm for a character <chi> and a normal subgroup <N>
##  proceeds as follows.
##  If <N> is abelian or has nilpotent factor then <chi> is relatively SM
##  with respect to <N>.
##  Otherwise we check whether <chi> restricts irreducibly to <N>; in this
##  case we also get a positive answer.
##  Otherwise a subnormal subgroup from that <chi> is induced must be
##  contained in a maximal normal subgroup of <N>.  So we get all maximal
##  normal subgroups containing <N> from that <chi> can be induced, take a
##  character that induces to <chi>, and check recursively whether it is
##  relatively subnormally monomial with respect to <N>.
##
##  For a group $G$ we consider only representatives of character orbits.
##
BindGlobal( "TestRelativelySMFun", function( arg )

    local test,      # result record
          G,         # argument, group
          chi,       # argument, character of `G'
          N,         # argument, normal subgroup of `G'
          n,         # classes in `N'
          t,         # character table of `G'
          nsg,       # list of normal subgroups of `G'
          newnsg,    # filtered list of normal subgroups
          orbits,    # orbits on `t.irreducibles'
          found,     # not relatively SM character found?
          i,         # loop over `nsg'
          j,         # loop over characters
          fus,       # fusion of conjugacy classes `N' in `G'
          norm,      # norm of restriction of `chi' to `N'
          isrelSM,   # is the constituent relatively SM?
          check,     #
          induced,   # is a subnormal subgroup found from where
                     # the actual character can be induced?
          k;         # loop over `newnsg'

    # step 1:
    # Check the arguments.
    if     Length( arg ) < 1 or 2 < Length( arg )
        or not ( IsGroup( arg[1] ) or IsCharacter( arg[1] ) ) then
      Error( "first argument must be a group or a character" );
    elif HasTestRelativelySM( arg[1] ) then
      return TestRelativelySM( arg[1] );
    fi;

    if IsGroup( arg[1] ) then
      G:= arg[1];
      Info( InfoMonomial, 1,
            "TestRelativelySM called with group ", GroupString( G, "G" ) );
    elif IsCharacter( arg[1] ) then
      G:= UnderlyingGroup( arg[1] );
      chi:= ValuesOfClassFunction( arg[1] );
      Info( InfoMonomial, 1,
            "TestRelativelySM called with character ",
            CharacterString( arg[1], "chi" ) );
    fi;

    # step 2:
    # Get the interesting normal subgroups.

    # We want to consider normal subgroups and factor groups.
    # If this test  yields a solution we can avoid to compute
    # the character table of `G'.
    # But if the character table of `G' is already known we use it
    # and store the factor groups.

    if   Length( arg ) = 1 then

      # If a normal subgroup <N> is abelian or has nilpotent factor group
      # then <G> is relatively SM w.r. to <N>, so consider only the other
      # normal subgroups.

      if HasOrdinaryCharacterTable( G ) then

        nsg:= ClassPositionsOfNormalSubgroups( CharacterTable( G ) );
        newnsg:= [];
        for n in nsg do
          if not CharacterTable_IsNilpotentFactor( CharacterTable( G ),
                     n ) then
            N:= NormalSubgroupClasses( CharacterTable( G ), n );
#T geht das?
#T        if IsSubset( n, centre ) and
            if not IsAbelian( N ) then
              Add( newnsg, N );
            fi;
          fi;
        od;
        nsg:= newnsg;

      else

        nsg:= NormalSubgroups( G );
        nsg:= Filtered( nsg, x -> not IsAbelian( x ) and
                                  not IsNilpotentGroup( G / x ) );

      fi;

    elif Length( arg ) = 2 then

      nsg:= [];

      if IsList( arg[2] ) then

        if not CharacterTable_IsNilpotentFactor( CharacterTable( G ),
                   arg[2] ) then
          N:= NormalSubgroupClasses( CharacterTable( G ), arg[2] );
          if not IsAbelian( N ) then
            nsg[1]:= N;
          fi;
        fi;

      elif IsGroup( arg[2] ) then

        N:= arg[2];
        if not IsAbelian( N ) and not IsNilpotentGroup( G / N ) then
          nsg[1]:= N;
        fi;

      else
        Error( "second argument must be normal subgroup or classes list" );
      fi;

    fi;

    # step 3:
    # Test whether all characters are relatively SM for all interesting
    # normal subgroups.

    if IsEmpty( nsg ) then

      test:= rec( isRelativelySM := true,
                  comment        :=
          "normal subgroups are abelian or have nilpotent factor group" );

    else

      t:= CharacterTable( G );
      if IsGroup( arg[1] ) then

        # Compute representatives of orbits of characters.
        orbits:= OrbitRepresentativesCharacters( Irr( t ) );
        orbits:= orbits{ [ 2 .. Length( orbits ) ] };

      else
        orbits:= [ chi ];
      fi;

      # Loop over all normal subgroups in `nsg' and all
      # irreducible characters in `orbits' until a not rel. SM
      # character is found.
      found:= false;
      i:= 1;
      while ( not found ) and i <= Length( nsg ) do

        N:= nsg[i];
        j:= 1;
        while ( not found ) and j <= Length( orbits ) do

#T use the kernel or centre here!!
#T if N does not contain the centre of chi then we need not test?
#T Isn't it sufficient to consider the factor modulo
#T the product of `N' and kernel of `chi'?
          chi:= orbits[j];

          # Is the restriction of `chi' to `N' irreducible?
          # This means we can choose $H = G$.
          n:= ClassPositionsOfNormalSubgroup( OrdinaryCharacterTable( G ),
                                              N );
          fus:= FusionConjugacyClasses( OrdinaryCharacterTable( N ),
                                        OrdinaryCharacterTable( G ) );
          norm:= Sum( n,
              c -> SizesConjugacyClasses( CharacterTable( G ) )[c] * chi[c]
                   * GaloisCyc( chi[c], -1 ), 0 );

          if norm = Size( N ) then

            test:= rec( isRelativelySM := true,
                        comment        := "irreducible restriction",
                        character      := Character( G, chi ) );

          else

            # If there is a subnormal subgroup $H$ from where <chi> is
            # induced then $H$ is contained in a maximal normal subgroup
            # of $G$ that contains <N>.

            # So compute all maximal subgroups ...
            newnsg:= ClassPositionsOfMaximalNormalSubgroups(
                         CharacterTable( G ) );

            # ... containing <N> ...
            newnsg:= Filtered( newnsg, x -> IsSubsetSet( x, n ) );

            # ... from where <chi> possibly can be induced.
            newnsg:= List( newnsg,
                           x -> TestInducedFromNormalSubgroup(
                                 Character( G, chi ),
                                 NormalSubgroupClasses( CharacterTable( G ),
                                                        x ) ) );

            induced:= false;
            k:= 1;
            while not induced and k <= Length( newnsg ) do

              check:= newnsg[k];
              if check.isInduced then

                # check whether the constituent is relatively SM w.r. to <N>
                isrelSM:= TestRelativelySM( check.character, N );
                if isrelSM.isRelativelySM then
                  induced:= true;
                fi;

              fi;
              k:= k+1;

            od;

            if induced then
              test:= rec( isRelativelySM := true,
                          comment := "suitable character found"
                         );
              if IsBound( isrelSM.character ) then
                test.character:= isrelSM.character;
              fi;
            else
              test:= rec( isRelativelySM := false,
                          comment := "all possibilities checked" );
            fi;

          fi;

          if not test.isRelativelySM then

            found:= true;
            test.character:= chi;
            test.normalSubgroup:= N;

          fi;

          j:= j+1;

        od;

        i:= i+1;

      od;

      if not found then

        # All characters are rel. SM w.r. to all normal subgroups.
        test:= rec( isRelativelySM := true,
                    comment        := "all possibilities checked" );
      fi;

    fi;

    Info( InfoMonomial, 1, "TestRelativelySM returns with `", test, "'" );
    return test;
end );

InstallMethod( TestRelativelySM,
    "for a character",
    [ IsClassFunction ],
    TestRelativelySMFun );

InstallMethod( TestRelativelySM,
    "for a group",
    [ IsGroup ],
    TestRelativelySMFun );

InstallOtherMethod( TestRelativelySM,
    "for a character, and an object",
    [ IsClassFunction, IsObject ],
    TestRelativelySMFun );

InstallOtherMethod( TestRelativelySM,
    "for a group, and an object",
    [ IsGroup, IsObject ],
    TestRelativelySMFun );


#############################################################################
##
#M  IsRelativelySM( <chi> )
#M  IsRelativelySM( <G> )
##
InstallMethod( IsRelativelySM,
    "for a character",
    [ IsClassFunction ],
    chi -> TestRelativelySM( chi ).isRelativelySM );

InstallOtherMethod( IsRelativelySM,
    "for a group",
    [ IsGroup ],
    G -> TestRelativelySM( G ).isRelativelySM );


#############################################################################
##
##  4. Minimal Nonmonomial Groups
##


#############################################################################
##
#M  IsMinimalNonmonomial( <G> ) . . . . . . . . . . .  for a (solvable) group
##
##  We use the classification by van der Waall.
##
InstallMethod( IsMinimalNonmonomial,
    "for a (solvable) group",
    [ IsGroup ],
    function( K )

    local F,          # Fitting subgroup
          factsize,   # index of `F' in `K'
          facts,      # prime factorization of the order of `F'
          p,          # prime dividing the order of `F'
          m,          # `F' is of order $p ^ m $
          syl,        # Sylow subgroup
          sylgen,     # one generator of `syl'
          gens,       # generators list
          C,          # centre of `K' in dihedral case
          fc,         # element in $F C$
          q;          # half of `factsize' in dihedral case

    # Check whether `K' is solvable.
    if not IsSolvableGroup( K ) then
      TryNextMethod();
    fi;

    # Compute the Fitting factor of the group.
    F:= FittingSubgroup( K );
    factsize:= Index( K, F );

    # The Fitting subgroup of a minimal nomonomial group is a $p$-group.
    facts:= Factors(Integers, Size( F ) );
    p:= Set( facts );
    if 1 < Length( p ) then
      return false;
    fi;
    p:= p[1];
    m:= Length( facts );

    # Check for the five possible structures.
    if   factsize = 4 then

      # If $K$ is minimal nonmonomial then
      # $K / F(K)$ is cyclic of order 4,
      # $F(K)$ is extraspecial of order $p^3$ and of exponent $p$
      # where $p \equiv -1 \pmod{4}$.

      if     IsPrimeInt( p )
         and p >= 3
         and ( p + 1 ) mod 4 = 0
         and m = 3
         and Centre( F ) = FrattiniSubgroup( F )
         and Size( Centre( F ) ) = p then

        # Check that the factor is cyclic and acts irreducibly.
        # For that, it is sufficient that the square acts
        # nontrivially.

        syl:= SylowSubgroup( K, 2 );
        if     IsCyclic( syl )
           and ForAny( GeneratorsOfGroup( syl ),
                       x ->     Order( x ) = 4
                            and ForAny( GeneratorsOfGroup( F ),
                                    y -> not IsOne( Comm( y, x^2 ) ) ) ) then
          SetIsMonomialGroup( K, false );
          return true;
        fi;

      fi;

    elif factsize = 8 then

      # If $K$ is minimal nonmonomial then
      # $K / F(K)$ is quaternion of order 8,
      # $F(K)$ is extraspecial of order $p^3$ and of exponent $p$
      # where $p \equiv 1 \pmod{4}$.

      if    IsPrimeInt( p )
         and p >= 5
         and ( p - 1 ) mod 4 = 0
         and m = 3
         and Centre( F ) = FrattiniSubgroup( F )
         and Size( Centre( F ) ) = p then

        # Check whether $K/F(K)$ is quaternion of order 8,
        # (i.e., is nonabelian with two *generators* of order 4 that do
        # not generate the same subgroup)
        # and that it acts irreducibly on $F$
        # For that, it is sufficient to show that the central involution
        # acts nontrivially.

        syl:= SylowSubgroup( K, 2 );
        gens:= Filtered( GeneratorsOfGroup( syl ), x -> Order( x ) = 4 );
        if     not IsAbelian( syl )
           and ForAny( gens,
                       x ->     x <> gens[1]
                            and x <> gens[1]^(-1)
                            and ForAny( GeneratorsOfGroup( F ),
                                    y -> not IsOne( Comm( y, x^2 ) ) ) ) then
          SetIsMonomialGroup( K, false );
          return true;
        fi;

      fi;

    elif factsize <> 2 and IsPrimeInt( factsize ) then

      # If $K$ is minimal nonmonomial then
      # $K / F(K)$ has order an odd prime $q$.
      # $F(K)$ is extraspecial of order $p^{2m+1}$ and of exponent $p$
      # where $2m$ is the order of $p$ modulo $q$.

      if    OrderMod( p, factsize ) = m-1
         and m mod 2 = 1
         and Centre( F ) = FrattiniSubgroup( F )
         and Size( Centre( F ) ) = p then

        # Furthermore, $F / Z(F)$ is a chief factor.
        # It is sufficient to show that the Fitting factor acts
        # trivially on $Z(F)$, and that there is no nontrivial
        # fixed point under the action on $F / Z(F)$.
        # These conditions are sufficient for our test.

        syl:= SylowSubgroup( K, factsize );
        sylgen:= First( GeneratorsOfGroup( syl ), g -> not IsOne( g ) );
        if     IsCentral( Centre( F ), syl )
           and ForAny( GeneratorsOfGroup( F ),
--> --------------------

--> maximum size reached

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

[ Dauer der Verarbeitung: 0.37 Sekunden  (vorverarbeitet)  ]