Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


SSL 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

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

[ Original von:0.64Diese Quellcodebibliothek enthält Beispiele in vielen Programmiersprachen. Man kann per Verzeichnistruktur darin navigieren. Der Code wird farblich markiert angezeigt.  Datei übertragen  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge