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


Quelle  test.g   Sprache: unbekannt

 
#############################################################################
##
#W  test.g               GAP 4 package AtlasRep                 Thomas Breuer
##
##  This file contains functions to test the data available in the
##  ATLAS of Group Representations or in private extensions.
##


#############################################################################
##
##  <#GAPDoc Label="tests">
##  The file <F>tst/testall.g</F> of the package
##  contains <Ref Func="Test" BookName="ref"/> statements
##  for checking whether the &AtlasRep; functions behave as documented.
##  One can run these tests by calling
##  <C>ReadPackage( "AtlasRep", "tst/testall.g" )</C>.
##  The examples in the package manual form a part of the tests,
##  they are collected in the file <F>tst/docxpl.tst</F> of the package.
##  <P/>
##  The remainder of this section deals with consistency checks of the data.
##  The tests described in Section
##  <Ref Subsect="subsect:AGR sanity checks by toc"/> can be used
##  for data from any extension of the database
##  (see Chapter <Ref Chap="chap:Private Extensions"/>),
##  Section <Ref Subsect="subsect:AGR other sanity checks"/> lists tests
##  which apply only to the core part of the database.
##  <P/>
##  All these tests apply only to <E>locally</E> available files
##  (see Section <Ref Sect="sect:The Tables of Contents of the AGR"/>),
##  no files are downloaded during the tests.
##  Thus the required space and time for running these tests
##  depend on the amount of locally available data.
##  <P/>
##  Some of the tests compute and verify additional data,
##  such as information about point stabilizers of permutation
##  representations.
##  In these cases, output lines starting with <C>#E</C> are error messages
##  that point to inconsistencies,
##  whereas output lines starting with <C>#I</C> inform about data that have
##  been computed and were not yet stored,
##  or about stored data that were not verified.
##  These tests are experimental in the sense that they involve several
##  heuristics.  Depending on the data to which they are applied,
##  it may happen that the tests run out of space or do not finish in
##  acceptable time.  Please inform the package maintainer if you run into
##  such problems.
##
##  <Subsection Label="subsect:AGR sanity checks by toc">
##  <Heading>Sanity Checks for a Table of Contents</Heading>
##
##  The following tests can be used to check the data that belong to a given
##  part of the database (core data or extension).
##  Each of these tests is given by a function with optional argument
##  <M>tocid</M>, the identifying string that had been entered as the second
##  argument of
##  <Ref Func="AtlasOfGroupRepresentationsNotifyData"
##  Label="for a local file describing private data"/>.
##  The contents of the core part can be checked by entering <C>"core"</C>,
##  which is also the default for <M>tocid</M>.
##  The function returns <K>false</K> if an error occurs,
##  otherwise <K>true</K>.
##  Currently the following tests of this kind are available.
##  (For some of them, the global option <C>TryToExtendData</C> can be
##  entered in order to try the computation of not yet stored data.)
##  <P/>
##  <List>
##  <#Include Label="test:AGR.Test.GroupOrders">
##  <!-- <Include Label="test:AGR.Test.GroupFlags"> -->
##  <#Include Label="test:AGR.Test.Words">
##  <#Include Label="test:AGR.Test.ClassScripts">
##  <#Include Label="test:AGR.Test.CycToCcls">
##  <#Include Label="test:AGR.Test.FileHeaders">
##  <#Include Label="test:AGR.Test.Files">
##  <#Include Label="test:AGR.Test.BinaryFormat">
##  <#Include Label="test:AGR.Test.Primitivity">
##  <#Include Label="test:AGR.Test.Characters">
##  <#Include Label="test:AGR.Test.StdCompatibility">
##  <#Include Label="test:AGR.Test.KernelGenerators">
##  <#Include Label="test:AGR.Test.MaxesOrders">
##  <#Include Label="test:AGR.Test.MaxesStructure">
##  <#Include Label="test:AGR.Test.MaxesStandardization">
##  <#Include Label="test:AGR.Test.CompatibleMaxes">
##  </List>
##
##  </Subsection>
##
##  <Subsection Label="subsect:AGR other sanity checks">
##  <Heading>Other Sanity Checks</Heading>
##
##  The tests described in this section are intended for checking data
##  that do not belong to a particular part of the &AtlasRep; database.
##  Therefore <E>all</E> locally available data are used in these tests.
##  Each of the tests is given by a function without arguments that
##  returns <K>false</K> if a contradiction was found during the test,
##  and <K>true</K> otherwise.
##  Additionally, certain messages are printed
##  when contradictions between stored and computed data are found,
##  when stored data cannot be verified computationally,
##  or when the computations yield improvements of the stored data.
##  Currently the following tests of this kind are available.
##  <P/>
##  <List>
##  <#Include Label="test:AGR.Test.Standardization">
##  <#Include Label="test:AGR.Test.StdTomLib">
##  <#Include Label="test:AGR.Test.MinimalDegrees">
##  </List>
##
##  </Subsection>
##  <#/GAPDoc>
##

if not IsPackageMarkedForLoading( "TomLib", "" ) then
  IsStandardGeneratorsOfGroup:= "dummy";
  LIBTOMKNOWN:= "dummy";
fi;

if not IsPackageMarkedForLoading( "CTblLib", "" ) then
  ConstructionInfoCharacterTable:= "dummy";
  HasConstructionInfoCharacterTable:= "dummy";
  LibInfoCharacterTable:= "dummy";
  StructureDescriptionCharacterTableName:= "dummy";
fi;

if not IsPackageMarkedForLoading( "Recog", "" ) then
  InfoRecog:= "dummy";
  RecogniseGroup:= "dummy";
  SLPforElement:= "dummy";
  NiceGens:= "dummy";
fi;


#############################################################################
##
AGR.FillHoles:= function( list, default )
    local i;

    for i in [ 1 .. Length( list ) ] do
      if not IsBound( list[i] ) then
        list[i]:= default;
      fi;
    od;
    return list;
end;
    
AGR.TOCLine:= function( tag, name, values, default )
    return Filtered( String( [ tag,
                         [ name, AGR.FillHoles( values, default ) ] ] ),
                     x -> x <> ' ' );
end;


#############################################################################
##
#V  AGR.Test
#V  AGR.Test.HardCases
#V  AGR.Test.HardCases.MaxNumberMaxes
#V  AGR.Test.HardCases.MaxNumberStd
#V  AGR.Test.HardCases.MaxNumberVersions
#V  AGR.Test.MaxTestDegree
##
##  'AGR.Test' is a record whose components belong to the various tests,
##  and list data which shall be omitted from the tests
##  because they would be too space or time consuming.
##
##  In the test loops, we assume upper bounds on the numbers of available
##  maximal subgroups and standardizations,
##  and we perform some tests only if a sufficiently small permutation
##  representation is available.
##
AGR.Test:= rec();
AGR.Test.HardCases:= rec();
AGR.Test.HardCases.MaxNumberMaxes:= 50;
AGR.Test.HardCases.MaxNumberStd:= 2;
AGR.Test.HardCases.MaxNumberVersions:= 3;
AGR.Test.MaxTestDegree:= 10^5;

#T 6.Suz.2 needs 200000 ...
#T 6.Fi22.2 needs ...


#############################################################################
##
#F  AGR.Test.Words( [<tocid>[, <gapname>]][,][<verbose>] )
##
##  <#GAPDoc Label="test:AGR.Test.Words">
##  <Mark><C>AGR.Test.Words( [</C><M>tocid</M><C>] )</C></Mark>
##  <Item>
##    processes the straight line programs that belong to <M>tocid</M>,
##    using the function stored in the <C>TestWords</C> component of the
##    data type in question.
##    <P/>
##    The straight line programs for the cases listed in
##    <C>AGR.Test.HardCases.TestWords</C> are omitted.
##  </Item>
##  <#/GAPDoc>
##
AGR.Test.HardCases.TestWords:= [
    [ "find", [ "B", "HN", "S417", "F24d2" ] ],
    [ "check", [ "B" ] ],
    [ "maxes", [ "Co1" ] ],
#T doable with recog?
  ];

AGR.Test.Words:= function( arg )
    local result, maxdeg, tocid, verbose, types, toc, name, r, type, omit,
          entry, prg, gens, grp, size;

    # Initialize the result.
    result:= true;

    maxdeg:= AGR.Test.MaxTestDegree;

    if Length( arg ) = 0 then
      return AGR.Test.Words( "core", false );
    elif Length( arg ) = 1 and IsBool( arg[1] ) then
      return AGR.Test.Words( "core", arg[1] );
    elif Length( arg ) = 1 and IsString( arg[1] ) then
      return AGR.Test.Words( arg[1], false );
    elif Length( arg ) = 2 and IsString( arg[1] ) and IsString( arg[2] ) then
      return AGR.Test.Words( arg[1], arg[2], false );
    elif Length( arg ) = 2 and IsString( arg[1] ) and IsBool( arg[2] ) then
      for name in AtlasOfGroupRepresentationsInfo.GAPnames do
        result:= AGR.Test.Words( arg[1],
                     name[1], arg[2] ) and result;
      od;
      return result;
    elif not ( Length( arg ) = 3 and IsString( arg[1] )
                                 and IsString( arg[2] )
                                 and IsBool( arg[3] ) ) then
      Error( "usage: AGR.Test.Words( [<tocid>[, ",
             "<gapname>]][,][<verbose>] )" );
    fi;

    tocid:= arg[1];
    verbose:= arg[3];

    # Check only straight line programs.
    types:= AGR.DataTypes( "prg" );
    name:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                  x -> x[1] = arg[2] );

    for toc in AGR.TablesOfContents( [ tocid, "local" ] ) do
      if IsBound( toc.( name[2] ) ) then
        r:= toc.( name[2] );

        # Note that the ordering in the 'and' statement must not be
        # changed, in order to execute all tests!
        for type in types do
          omit:= First( AGR.Test.HardCases.TestWords,
                        pair -> pair[1] = type[1] );
          if IsBound( r.( type[1] ) ) then
            if IsList( omit ) and name[2] in omit[2] then
              if verbose then
                Print( "#I  AGR.Test.Words:\n",
                       "#I  omit TestWords for ", type[1], " and ", name[2],
                       "\n" );
              fi;
            else
              for entry in r.( type[1] ) do
                result:= type[2].TestWords( tocid, name[2],
                             entry[ Length( entry ) ], type, verbose )
                         and result;
              od;
            fi;
          fi;
        od;

        # Check also those 'maxext' scripts (which do not form a data type)
        # that belong to the given t.o.c.
        r:= name[3];
        if IsBound( r.maxext ) then
          for entry in Filtered( r.maxext, l -> l[4] = tocid ) do
            prg:= AtlasProgram( name[1], entry[1], "maxes", entry[2] );
            if prg = fail then
              if verbose then
                Print( "#E  AGR.Test.Words:\n",
                       "#E  cannot verify 'maxext' entry '", entry[3], "'\n" );
                result:= false;
              fi;
            elif not IsInternallyConsistent( prg.program )  then
              Print( "#E  AGR.Test.Words:\n",
                     "#E  program '", entry[3],
                     "' not internally consistent\n" );
              result:= false;
            else
              # Get a representation if available, and map the generators.
              gens:= OneAtlasGeneratingSetInfo( prg.groupname,
                         prg.standardization,
                         NrMovedPoints, [ 2 .. maxdeg ],
                         "contents", [ tocid, "local" ] );
              if gens = fail then
                if verbose then
                  Print( "#I  AGR.Test.Words:\n",
                         "#I  no perm. repres. for '", prg.groupname,
                         "', no check for '", entry[3], "'\n" );
                fi;
              else
                gens:= AtlasGenerators( gens );
                grp:= Group( gens.generators );
                if IsBound( gens.size ) then
                  SetSize( grp, gens.size );
                fi;
                gens:= ResultOfStraightLineProgram( prg.program,
                           gens.generators );
                size:= Size( SubgroupNC( grp, gens ) );
#T use the recog package for larger cases!
                if IsBound( prg.size ) then
                  if size <> prg.size then
                    Print( "#E  AGR.Test.Words:\n",
                           "#E  program '", entry[3], "' for group of order ",
                           size, " not ", prg.size, "\n" );
                    result:= false;
                  fi;
                else
                  Print( "#I  AGR.Test.Words:\n",
                         "#I  add size ", size, " for program '", entry[3],
                         "'\n" );
                fi;
              fi;
            fi;
          od;
        fi;
      fi;
    od;

    # Return the result.
    return result;
    end;


#############################################################################
##
#F  AGR.Test.FileHeaders( [<tocid>[,<gapname>]] )
##
##  <#GAPDoc Label="test:AGR.Test.FileHeaders">
##  <Mark><C>AGR.Test.FileHeaders( [</C><M>tocid</M><C>] )</C></Mark>
##  <Item>
##    checks whether the &MeatAxe; text files that belong to <M>tocid</M>
##    have a header line that is consistent with the filename,
##    and whether the contents of all &GAP; format data files that belong to
##    <M>tocid</M> is consistent with the filename.
##  </Item>
##  <#/GAPDoc>
##
AGR.Test.FileHeaders:= function( arg )
    local result, name, toc, record, type, entry, test;

    # Initialize the result.
    result:= true;

    if Length( arg ) = 2 then
      name:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                    x -> x[1] = arg[2] );
      for toc in AGR.TablesOfContents( [ arg[1], "local" ] ) do
        if IsBound( toc.( name[2] ) ) then
          record:= toc.( name[2] );
          for type in AGR.DataTypes( "rep" ) do
            if IsBound( record.( type[1] ) ) then
              for entry in record.( type[1] ) do
                test:= type[2].TestFileHeaders( arg[1], arg[2], entry, type );
                if not IsBool( test ) then
                  Print( "#E  AGR.Test.FileHeaders:\n",
                         "#E  ", test, " for ", entry[ Length( entry ) ],
                         "\n" );
                  test:= false;
                fi;
                result:= test and result;
              od;
            fi;
          od;
        fi;
      od;
    elif Length( arg ) = 1 then
      for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
        result:= AGR.Test.FileHeaders( arg[1], entry[1] ) and result;
      od;
    elif Length( arg ) = 0 then
      result:= AGR.Test.FileHeaders( "core" );
    fi;

    # Return the result.
    return result;
    end;


#############################################################################
##
#F  AGR.Test.BinaryFormat( [<tocid>] )
##
##  <#GAPDoc Label="test:AGR.Test.BinaryFormat">
##  <Mark><C>AGR.Test.BinaryFormat( [</C><M>tocid</M><C>] )</C></Mark>
##  <Item>
##    checks whether all &MeatAxe; text files that belong to <M>tocid</M>
##    satisfy that applying first <Ref Func="CMtxBinaryFFMatOrPerm"/> and
##    then <Ref Func="FFMatOrPermCMtxBinary"/> yields the same object.
##  </Item>
##  <#/GAPDoc>
##
AGR.Test.BinaryFormat:= function( arg )
    local tmpfile, tocid, result, r, gens, gen, test, cnv;

    # Create one temporary file.
    tmpfile:= Filename( DirectoryTemporary(), "testfile" );

    # Get the data directory.
    if IsEmpty( arg ) then
      tocid:= [ "core", "local" ];
    else
      tocid:= arg[1];
    fi;

    result:= true;

    for r in Concatenation( AllAtlasGeneratingSetInfos( "contents", tocid,
                                IsPermGroup, true ),
                            AllAtlasGeneratingSetInfos( "contents", tocid,
                                Characteristic, IsPosInt ) ) do
      gens:= AtlasGenerators( r );
      if gens <> fail then
        gens:= gens.generators;
        for gen in gens do
          test:= false;
          if IsPerm( gen ) then
            CMtxBinaryFFMatOrPerm( gen, LargestMovedPoint( gen ), tmpfile );
            test:= true;
          elif IsMatrix( gen ) then
            cnv:= ConvertToMatrixRep( gen );
            if IsInt( cnv ) then
              CMtxBinaryFFMatOrPerm( gen, cnv, tmpfile );
              test:= true;
            fi;
          else
            Print( "#E  AGR.Test.BinaryFormat:\n",
                   "#E  not permutation or matrix for '", r, "'\n" );
            result:= false;
          fi;
          if test and gen <> FFMatOrPermCMtxBinary( tmpfile ) then
            Print( "#E  AGR.Test.BinaryFormat:\n",
                   "#E  differences for '", r, "'\n" );
            result:= false;
          fi;
        od;
      fi;
    od;

    # Remove the temporary file.
    RemoveFile( tmpfile );

    # Return the result.
    return result;
    end;


#############################################################################
##
#F  AGR.Test.Standardization( [<gapname>] )
##
##  <#GAPDoc Label="test:AGR.Test.Standardization">
##  <Mark><C>AGR.Test.Standardization()</C></Mark>
##  <Item>
##    checks whether all generating sets corresponding to the same set of
##    standard generators have the same element orders; for the case that
##    straight line programs for computing certain class representatives are
##    available, also the orders of these representatives are checked
##    w. r. t. all generating sets.
##  </Item>
##  <#/GAPDoc>
##
AGR.Test.Standardization:= function( arg )
    local result, name, gapname, gensorders, cclorders, cycorders, tbl, info,
          gens, std, ords, pair, prg, names, choice;

    # Initialize the result.
    result:= true;

    if Length( arg ) = 0 then

      for name in AtlasOfGroupRepresentationsInfo.GAPnames do
        result:= AGR.Test.Standardization( name[1] ) and result;
      od;

    elif Length( arg ) = 1 and IsString( arg[1] ) then

      gapname:= arg[1];
      if AGR.InfoForName( gapname ) = fail then
        Print( "#E  AGR.Test.Standardization:\n",
               "#E  no group with GAP name '", gapname, "'\n" );
        return false;
      fi;

      gensorders:= [];
      cclorders:= [];
      cycorders:= [];

      tbl:= CharacterTable( gapname );

      # Loop over the relevant representations.
      for info in AllAtlasGeneratingSetInfos( gapname, "contents", "local" ) do
        gens:= AtlasGenerators( info.identifier );
        std:= gens.standardization;

        # Check that the generators are invertible,
        # and that the orders are equal in all representations.
        if ForAll( gens.generators, x -> Inverse( x ) <> fail ) then
          ords:= List( gens.generators, Order );
        else
          ords:= [ fail ];
        fi;
        if not ForAll( ords, IsInt ) then
          Print( "#E  AGR.Test.Standardization:\n",
                 "#E  representation '", gens.identifier[2],
                 "': non-finite order\n" );
          result:= false;
        elif IsBound( gensorders[ std+1 ] ) then
          if gensorders[ std+1 ] <> ords then
            Print( "#E  AGR.Test.Standardization:\n",
                   "#E  '", gapname, "': representation '",
                   gens.identifier[2], "':\n",
                   "#E  incompatible generator orders ",
                   ords, " and ", gensorders[ std+1 ], "\n" );
            result:= false;
          fi;
        else
          gensorders[ std+1 ]:= ords;
        fi;

        # If scripts for computing representatives of cyclic subgroups
        # or representatives of conjugacy classes are available
        # then check that their orders are equal in all representations.
        for pair in [ [ cclorders, "classes" ], [ cycorders, "cyclic" ] ] do
          if not IsBound( pair[1][ std+1 ] ) then
            prg:= AtlasProgram( gapname, std, pair[2] );
            if prg = fail then
              pair[1][ std+1 ]:= fail;
            else
              pair[1][ std+1 ]:= [ prg.program,
                                   List( ResultOfStraightLineProgram(
                                     prg.program, gens.generators ), Order ) ];
              if tbl <> fail then
                names:= AtlasClassNames( tbl );
                if IsBound( prg.outputs ) then
                  choice:= List( prg.outputs, x -> Position( names, x ) );
                  if ( not fail in choice ) and pair[1][ std+1 ][2]
                         <> OrdersClassRepresentatives( tbl ){ choice } then
                    Print( "#E  AGR.Test.Standardization:\n",
                           "#E  '", gapname, "': representation '",
                           gens.identifier[2], "':\n",
                           "#E  ", pair[2],
                           " orders differ from character table\n" );
                    result:= false;
                  fi;
                else
                  Print( "#E  no component 'outputs' in '", pair[2],
                         "' for '", gapname, "'\n" );
                fi;
              fi;
            fi;
          elif pair[1][ std+1 ] <> fail then
            if pair[1][ std+1 ][2] <> List( ResultOfStraightLineProgram(
                   pair[1][ std+1 ][1], gens.generators ), Order ) then
              Print( "#E  AGR.Test.Standardization:\n",
                     "#E  '", gapname, "': representation '",
                     gens.identifier[2], "':\n",
                     "#E  incompatible ", pair[2], " orders\n" );
              result:= false;
            fi;
          fi;
        od;
      od;

    fi;

    # Return the result.
    return result;
    end;


#############################################################################
##
#F  AGR.Test.StdTomLib( [<gapname>] )
##
##  <#GAPDoc Label="test:AGR.Test.StdTomLib">
##  <Mark><C>AGR.Test.StdTomLib()</C></Mark>
##  <Item>
##    checks whether the standard generators are compatible with those that
##    occur in the <Package>TomLib</Package> package.
##  </Item>
##  <#/GAPDoc>
##
AGR.Test.StdTomLib:= function( arg )
    local result, name, tomnames, tbl, tom, gapname, info, allgens, stdavail,
          verified, falsified, G, i, iinfo, type, prg, res, gens, G2,
          fitstotom, fitstohom;

    if not IsPackageMarkedForLoading( "TomLib", "1.0" ) then
      Print( "#E  AGR.Test.StdTomLib:\n",
             "#E  TomLib not loaded, cannot verify ATLAS standardizations\n" );
      return false;
    fi;

    # Initialize the result.
    result:= true;

    if Length( arg ) = 0 then

      for name in AtlasOfGroupRepresentationsInfo.GAPnames do
        result:= AGR.Test.StdTomLib( name[1] ) and result;
      od;

      # Check also that all tables of marks which provide standardization
      # information really belong to ATLAS groups.
      tomnames:= Set( List( Filtered( LIBTOMKNOWN.STDGEN, x -> x[2] <> "N" ),
                            x -> x[1] ) );
      for name in AtlasOfGroupRepresentationsInfo.GAPnames do
        tbl:= CharacterTable( name[1] );
        if tbl <> fail then
          tom:= TableOfMarks( tbl );
          if tom <> fail then
            RemoveSet( tomnames, Identifier( tom ) );
          fi;
        fi;
      od;

      if not IsEmpty( tomnames ) then
        Print( "#E  AGR.Test.StdTomLib:\n",
               "#E  cannot verify ATLAS standardizations for tables of ",
               "marks in\n",
               "#E  ", tomnames, "\n" );
        result:= false;
      fi;

    elif Length( arg ) = 1 and IsString( arg[1] ) then

      gapname:= arg[1];
      if AGR.InfoForName( gapname ) = fail then
        Print( "#E  AGR.Test.StdTomLib:\n",
               "#E  no group with GAP name '", gapname, "'\n" );
        return false;
      fi;

      tbl:= CharacterTable( gapname );

      # Check the ATLAS standardization against the TomLib standardization.
      # (We consider only ATLAS permutation representations.)
      if tbl = fail then
        tom:= fail;
      else
        tom:= TableOfMarks( tbl );
      fi;
      if tom <> fail then

        # The table of marks is available,
        # which implies that the TomLib package is loaded.
        # Thus '(Has)StandardGeneratorsInfo' is bound.
        # (But avoid a syntax error message when reading the file
        # in the case that TomLib is not available.)
        if ValueGlobal( "HasStandardGeneratorsInfo" )( tom ) then
          info:= ValueGlobal( "StandardGeneratorsInfo" )( tom );
        else
          info:= [];
        fi;

        allgens:= AllAtlasGeneratingSetInfos( gapname, IsPermGroup, true,
                                              "contents", "local" );
        stdavail:= Set( List( allgens, x -> x.standardization ) );

        if not IsSubset( stdavail,
                         Set( List( info, r -> r.standardization ) ) ) then
          Print( "#E  AGR.Test.StdTomLib:\n",
                 "#E  strange standardization info ",
                 " for table of marks of ", gapname, "\n" );
          result:= false;
        fi;

        if not IsSubset( Set( List( info, r -> r.standardization ) ),
                         stdavail ) then
          Print( "#I  AGR.Test.StdTomLib:\n",
                 "#I  extend STDGEN info for ", gapname, "\n" );
        fi;

        allgens:= List( stdavail,
                        i -> First( allgens, x -> x.standardization = i ) );
        verified:= [];
        falsified:= [];
        G:= UnderlyingGroup( tom );

        for i in Union( stdavail, List( info, r -> r.standardization ) ) do

          # 1. Apply AtlasRep checks (using 'pres' and 'check' scripts)
          #    to the TomLib generators.
          iinfo:= First( info, r -> IsBound( r.standardization ) and
                                    r.standardization = i );
          if i in stdavail then
            for type in [ "pres", "check" ] do
              prg:= AtlasProgram( gapname, i, type );
              if prg <> fail then
                res:= ResultOfStraightLineDecision( prg.program,
                          GeneratorsOfGroup( G ) );
                if res = true then
                  AddSet( verified, i );
                  if iinfo = fail then
                    Print( "#I  AGR.Test.StdTomLib:\n",
                           "#I  ", gapname,
                           ": extend TomLib standardization info, ",
                           "we have standardization = ", i, "\n" );
                  elif ForAny( info, r -> IsBound( r.standardization ) and
                                          r.standardization <> i ) then
                    Print( "#E  AGR.Test.StdTomLib:\n",
                           "#E  ", gapname,
                           ": different TomLib standardizations (", i,
                           " verified)?\n" );
                    result:= false;
                  fi;
                else
                  AddSet( falsified, i );
                  if iinfo <> fail then
                    Print( "#E  AGR.Test.StdTomLib:\n",
                           "#E  ", gapname,
                           ": TomLib standardization info is not ", i, "\n" );
                    result:= false;
                  fi;
                fi;
              fi;
            od;
          fi;

          # 2. Apply TomLib checks to the Atlas generators
          #    (permutations only).
          if iinfo.script = fail then
            Print( "#E  AGR.Test.StdTomLib:\n",
                   "#E  ", gapname,
                   ": script component 'fail' in TomLib standardization\n" );
          else
            # Compare the available ATLAS generators
            # with this TomLib standardization.
            for gens in allgens do
              gens:= AtlasGenerators( gens.identifier );
              G2:= Group( gens.generators );
              fitstotom:= IsStandardGeneratorsOfGroup( info, G2,
                              gens.generators );
              fitstohom:= GroupHomomorphismByImages( G, G2,
                              GeneratorsOfGroup( G ), gens.generators )
                          <> fail;
              if fitstotom <> fitstohom then
                Print( "#E  AGR.Test.StdTomLib:\n",
                       "#E  ", gapname,
                       ": IsStandardGeneratorsOfGroup and ",
                       "homom. construction for standardization ",
                       gens.standardization, " inconsistent\n" );
              fi;

              if fitstotom then
                AddSet( verified, gens.standardization );
                if IsBound( info.standardization ) then
                  if info.standardization <> gens.standardization then
                    Print( "#I  AGR.Test.StdTomLib:\n",
                           "#I  ", gapname,
                           ": TomLib standardization is ",
                           gens.standardization, " not ",
                           info.standardization, "\n" );
                    result:= false;
                  fi;
                else
                  Print( "#I  AGR.Test.StdTomLib:\n",
                         "#I  ", gapname,
                         ": TomLib standardization is ",
                         gens.standardization, "\n" );
                fi;
              else
                AddSet( falsified, gens.standardization );
                if IsBound( info.standardization ) and
                   info.standardization = gens.standardization then
                  Print( "#E  AGR.Test.StdTomLib:\n",
                         "#E  ", gapname,
                         ": TomLib standardization is not ",
                         info.standardization, "\n" );
                fi;
              fi;
            od;
          fi;
        od;

        # Now 'verified' and 'falsified' are the lists of standardizations
        # that hold or do not hold, respectively, for the generators of 'G'.
        if IsEmpty( info ) then
            Print( "#I  AGR.Test.StdTomLib:\n",
                   "#I  ", gapname, ": add TomLib info!\n" );
        fi;

        if IsSubset( falsified, stdavail ) and
           ForAny( info, r -> r.ATLAS <> false ) then
          Print( "#E  AGR.Test.StdTomLib:\n",
                 "#E  ", gapname,
                 ": TomLib standardization info must be ATLAS = \"N\"\n" );
        fi;

        if ( not IsSubset( falsified, stdavail ) ) and
           ForAny( info, r -> r.ATLAS = false ) then
          Print( "#E  AGR.Test.StdTomLib:\n",
                 "#E  ", gapname,
                 ": cannot verify TomLib info ATLAS = \"N\"\n" );
        fi;

      fi;

    fi;

    # Return the result.
    return result;
    end;


#############################################################################
##
#F  AGR.Test.Files( [<tocid>[, <gapname>]] )
##
##  <#GAPDoc Label="test:AGR.Test.Files">
##  <Mark><C>AGR.Test.Files( [</C><M>tocid</M><C>] )</C></Mark>
##  <Item>
##    checks whether the &MeatAxe; text files that belong to <M>tocid</M>
##    can be read with <Ref Func="ScanMeatAxeFile"/> such that the result
##    is not <K>fail</K>.
##    The function does not check whether the first line of a &MeatAxe; text
##    file is consistent with the filename, since this can be tested with
##    <C>AGR.Test.FileHeaders</C>.
##  </Item>
##  <#/GAPDoc>
##
AGR.Test.Files:= function( arg )
    local result, entry, name, toc, record, type;

    # Initialize the result.
    result:= true;

    if IsEmpty( arg ) then
      result:= AGR.Test.Files( "core" );
    elif Length( arg ) = 1 then
      for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
        result:= AGR.Test.Files( arg[1], entry[1] ) and result;
      od;
    elif Length( arg ) = 2 then
      name:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                    x -> x[1] = arg[2] );
      if name = fail then
        return result;
      fi;
      name:= name[2];
      for toc in AGR.TablesOfContents( [ arg[1], "local" ] ) do
        if IsBound( toc.( name ) ) then
          record:= toc.( name );
          for type in AGR.DataTypes( "rep" ) do
            if IsBound( record.( type[1] ) ) then
              for entry in record.( type[1] ) do
                result:= type[2].TestFiles( arg[1], name, entry, type )
                         and result;
              od;
            fi;
          od;
        fi;
      od;
    fi;

    # Return the result.
    return result;
    end;


#############################################################################
##
#F  AGR.Test.ClassScripts( [<tocid>[, <gapname>]] )
##
##  <#GAPDoc Label="test:AGR.Test.ClassScripts">
##  <Mark><C>AGR.Test.ClassScripts( [</C><M>tocid</M><C>] )</C></Mark>
##  <Item>
##    checks whether the straight line programs that belong to <M>tocid</M>
##    and that compute representatives of certain conjugacy classes
##    are consistent with information stored on the &GAP; character table
##    of the group in question, in the sense that
##    the given class names really occur in the character table and that
##    the element orders and centralizer orders for the classes are correct.
##  </Item>
##  <#/GAPDoc>
##
AGR.Test.ClassScripts:= function( arg )
    local result, maxdeg, entry, tocid, gapname, groupname, toc, record,
          std, name, prg, tbl, outputs, ident, classnames, map, gens, roots,
          grp, reps, orders1, orders2, cents1, cents2, cycscript;

    # Initialize the result.
    result:= true;
    maxdeg:= AGR.Test.MaxTestDegree;

    if IsEmpty( arg ) then
      return AGR.Test.ClassScripts( "core" );
    elif Length( arg ) = 1 and IsString( arg[1] ) then
      # The argument is an identifier of an extension.
      for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
        result:= AGR.Test.ClassScripts( arg[1], entry[1] ) and result;
      od;
      return result;
    elif Length( arg ) = 2 and IsString( arg[1] ) and IsString( arg[2] ) then
      # The arguments are an identifier and a group name.
      tocid:= arg[1];
      gapname:= arg[2];
    else
      Error( "usage: AGR.Test.ClassScripts( [<tocid>[, <groupname>]] )" );
    fi;

    groupname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                       pair -> pair[1] = gapname );
    if groupname = fail then
      Print( "#E  AGR.Test.ClassScripts:\n",
             "#E  no group with name '", gapname, "'\n" );
      return false;
    fi;
    groupname:= groupname[2];
    for toc in AGR.TablesOfContents( [ tocid, "local" ] ) do
      if IsBound( toc.( groupname ) ) then
        record:= toc.( groupname );
        for name in [ "cyclic", "classes", "cyc2ccl" ] do
          if IsBound( record.( name ) ) then
            for std in Set( List( record.( name ), x -> x[1] ) ) do

              prg:= AtlasProgram( gapname, std, name );
              if prg = fail then
                Print( "#E  AGR.Test.ClassScripts:\n",
                       "#E  inconsistent program '", name, "' for '",
                       gapname, "'\n" );
                result:= false;
              else

                # Fetch the character table of the group.
                # (No further tests are possible if it is not available.)
                tbl:= CharacterTable( gapname );
                if tbl <> fail then

                  ident:= prg.identifier[2];
                  classnames:= AtlasClassNames( tbl );
                  if classnames <> fail then
                    if IsBound( prg.outputs ) then
                      outputs:= prg.outputs;
                      map:= List( outputs, x -> Position( classnames, x ) );
                    else
                      Print( "#E  AGR.Test.ClassScripts:\n",
                             "#E  no component 'outputs' in '", name,
                             "' for '", gapname, "'\n" );
                      result:= false;
                      outputs:= [ "-" ];
                      map:= [ fail ];
                    fi;
                    prg:= prg.program;

                    # (If '-' signs occur then we cannot test the names,
                    # but the number of outputs can be checked.)
                    roots:= ClassRoots( tbl );
                    roots:= Filtered( [ 1 .. Length( roots ) ],
                                      i -> IsEmpty( roots[i] ) );
                    roots:= Set( List( roots, x -> ClassOrbit( tbl, x ) ) );

                    if ForAll( outputs, x -> not '-' in x ) then

                      # Check the class names.
                      if fail in map then
                        Print( "#E  AGR.Test.ClassScripts:\n",
                               "#E  strange class names ",
                               Difference( outputs, classnames ),
                               " for program ", ident, "\n" );
                        result:= false;
                      fi;
                      if     name in [ "classes", "cyc2ccl" ]
                         and Set( classnames ) <> Set( outputs ) then
                        Print( "#E  AGR.Test.ClassScripts:\n",
                               "#E  class names ",
                               Difference( classnames, outputs ),
                               " not hit for program ", ident, "\n" );
                        result:= false;
                      fi;
                      if name = "cyclic" then
                        # Check whether all maximally cyclic subgroups
                        # are covered.
                        roots:= Filtered( roots,
                                   list -> IsEmpty( Intersection( outputs,
                                               classnames{ list } ) ) );
                        if not IsEmpty( roots ) then
                          Print( "#E  AGR.Test.ClassScripts:\n",
                                 "#E  maximally cyclic subgroups ",
                                 List( roots, x -> classnames{ x } ),
                                 " not hit for program ", ident, "\n" );
                          result:= false;
                        fi;
                      fi;

                    elif name = "cyclic" and
                         Length( outputs ) <> Length( roots ) and
                         not ForAny( outputs, x -> '-' in x ) then
                      # The programs 'F23G1-cycW1' and 'F24G1-cycW1'
                      # specify some elements only up to Galois conjugacy.
                      Print( "#E  AGR.Test.ClassScripts:\n",
                             "#E  no. of outputs and cyclic subgroups differ",
                             " for program '", ident, "'\n" );
                      result:= false;
                    fi;

                    if not fail in map then

                      # Compute the representatives in a representation.
                      # (No further tests are possible if none is available.)
                      gens:= OneAtlasGeneratingSetInfo( gapname, std,
                                 NrMovedPoints, [ 2 .. maxdeg ],
                                 "contents", [ tocid, "local" ] );
                      if gens <> fail then

                        gens:= AtlasGenerators( gens.identifier );
                        if gens <> fail then
                          gens:= gens.generators;
                        fi;
                        if fail in gens then
                          gens:= fail;
                        fi;

                        if not name in [ "cyclic", "classes" ] then

                          # The input consists of the images of the standard
                          # generators under the 'cyc' script (which may belong
                          # to a different t.o.c.).
                          cycscript:= AtlasProgram( gapname, std, "cyclic",
                              "version", AGR.VersionOfSLP( ident )[1],
                              "contents", [ tocid, "local" ] );
                          if cycscript = fail then
                            gens:= fail;
                            Print( "#E  AGR.Test.ClassScripts:\n",
                                   "#E  no script for computing the 'cyc' ",
                                   "part of '", ident, "' available\n" );
                            result:= false;
                          elif gens <> fail then
                            gens:= ResultOfStraightLineProgram(
                                       cycscript.program, gens );
                          fi;
                        fi;

                      fi;

                      if gens <> fail then

                        grp:= Group( gens );
                        reps:= ResultOfStraightLineProgram( prg, gens );

                        if Length( reps ) <> Length( outputs ) then
                          Print( "#E  AGR.Test.ClassScripts:\n",
                                 "#E  inconsistent output numbers for ",
                                 "program ", ident, "\n" );
                          result:= false;
                        else

                          # Check element orders and centralizer orders.
                          orders1:= OrdersClassRepresentatives( tbl ){ map };
                          orders2:= List( reps, Order );
                          if orders1 <> orders2 then
                            Print( "#E  AGR.Test.ClassScripts:\n",
                                   "#E  element orders of ",
                                   outputs{ Filtered( [ 1 .. Length( outputs ) ],
                                              i -> orders1[i] <> orders2[i] ) },
                                   " differ for program ", ident, "\n" );
                            result:= false;
                          fi;
                          cents1:= SizesCentralizers( tbl ){ map };
                          cents2:= List( reps, x -> Size( Centralizer(grp,x) ) );
                          if    cents1 <> cents2 then
                            Print( "#E  AGR.Test.ClassScripts:\n",
                                   "#E  centralizer orders of ",
                                   outputs{ Filtered( [ 1 .. Length( outputs ) ],
                                              i -> cents1[i] <> cents2[i] ) },
                                   " differ for program ", ident, "\n" );
                            result:= false;
                          fi;

                        fi;

                      fi;

                    fi;
                  fi;

                fi;
              fi;

            od;
          fi;
        od;
      fi;
    od;

    # Return the result.
    return result;
    end;


#############################################################################
##
#F  AGR.Test.CycToCcls( [<tocid>[, <gapname>]] )
##
##  <#GAPDoc Label="test:AGR.Test.CycToCcls">
##  <Mark><C>AGR.Test.CycToCcls( [</C><M>tocid</M><C>][:TryToExtendData] )</C></Mark>
##  <Item>
##    checks whether all straight line programs that belong to <M>tocid</M>
##    and that compute class representatives from representatives of cyclic
##    subgroups possess a corresponding straight line program
##    (<E>anywhere</E> in the database)
##    for computing representatives of cyclic subgroups.
##  </Item>
##  <#/GAPDoc>
##
##    if the extend option is set then:
##    checks whether some straight line program that computes representatives
##    of conjugacy classes of a group can be computed from the ordinary
##    &GAP; character table of that group and a straight line program in
##    <M>tocid</M> that computes representatives of cyclic subgroups.
##    In this case the missing scripts are printed.
##
AGR.Test.CycToCcls:= function( arg )
    local result, triple, tocid, groupname, gapname, toc, record, entry,
          versions, prg, tbl, version, str;

    # Initialize the result.
    result:= true;

    if IsEmpty( arg ) then
      return AGR.Test.CycToCcls( "core" );
    elif Length( arg ) = 1 and IsString( arg[1] ) then
      # The argument is an identifier of an extension.
      for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
        result:= AGR.Test.CycToCcls( arg[1], entry[1] ) and result;
      od;
      return result;
    elif Length( arg ) = 2 and IsString( arg[1] ) and IsString( arg[2] ) then
      # The arguments are an identifier and a group name.
      tocid:= arg[1];
      gapname:= arg[2];
    else
      Error( "usage: AGR.Test.CycToCcls( [<tocid>[, <gapname>]] )" );
    fi;

    groupname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                       pair -> pair[1] = gapname );
    if groupname = fail then
      Print( "#E  AGR.Test.CycToCcls:\n",
             "#E  no group with name '", gapname, "'\n" );
      return false;
    fi;
    groupname:= groupname[2];
    for toc in AGR.TablesOfContents( tocid ) do
      if not IsBound( toc.( groupname ) ) then
        return true;
      fi;
      record:= toc.( groupname );

      # Run over the 'cyc2ccl' scripts that are available in this t.o.c,
      # and check whether *some t.o.c.* provides the corresponding 'cyc' script.
      if IsBound( record.cyc2ccl ) then
        for entry in record.cyc2ccl do
          versions:= AGR.VersionOfSLP( entry[2] );
          prg:= AtlasProgramInfo( gapname, "cyclic", "version", versions[1] );
          if prg = fail then
            Print( "#E  AGR.Test.CycToCcls:\n",
                   "#E  no program '\"",
                   ReplacedString( entry[2]{
                                       [ 1 .. Position( entry[2], '-' )-1 ] },
                                   "cyc", "-cyc" ), "\"' available\n" );
            result:= false;
          fi;
        od;
      fi;

      if ValueOption( "TryToExtendData" ) <> true then
        return result;
      fi;

      # Check whether the current t.o.c. contains a 'cyc' script for which
      # no 'cyc2ccl' script is available in *any t.o.c.* but for which such a
      # script can be computed.
      # (This is possible only if we have the character table of the group.)
      tbl:= CharacterTable( gapname );
      if tbl <> fail and IsBound( record.cyclic ) then
        for entry in record.cyclic do
          version:= AGR.VersionOfSLP( entry[2] );
          prg:= AtlasProgram( gapname, "cyc2ccl", version,
                              "contents", "local" );
          if prg = fail then
            # There is no 'cyc2ccl' script but perhaps we can create it.
            prg:= AtlasProgram( gapname, "cyclic", version,
                                "contents", "local" );
            if prg = fail then
              Print( "#E  AGR.Test.CycToCcls:\n",
                     "#E  cannot access program '\"", entry[2], "\"\n" );
              result:= false;
            else
              str:= StringOfAtlasProgramCycToCcls(
                        AtlasStringOfProgram( prg.program, prg.outputs ),
                        tbl, "names" );
              if str <> fail then
                prg:= ScanStraightLineProgram( str, "string" );
                if prg = fail then
                  Print( "#E  AGR.Test.CycToCcls:\n",
                         "#E  automatically created cyc2ccl script for '",
                         entry[2], "' would be incorrect" );
                  result:= false;
                else
                  prg:= prg.program;
                  Print( "#I  AGR.Test.CycToCcls:\n",
                         "#I  add the following script, in the new file '",
                         ReplacedString( entry[2], "-", "" ), "-cclsW1':\n",
                         str, "\n" );
                fi;
              fi;
            fi;
          fi;
        od;
      fi;
    od;

    # Return the result.
    return result;
    end;


#############################################################################
##
#F  AGR.Test.GroupOrders( [true] )
##
##  <#GAPDoc Label="test:AGR.Test.GroupOrders">
##  <Mark><C>AGR.Test.GroupOrders()</C></Mark>
##  <Item>
##    checks whether the group orders stored in the <C>GAPnames</C> component
##    of <Ref Var="AtlasOfGroupRepresentationsInfo"/> coincide with the
##    group orders computed from an &ATLAS; permutation representation of
##    degree up to <C>AGR.Test.MaxTestDegree</C>,
##    from the available character table or table of marks with the given name,
##    or from the structure of the name,
##    in the sense that splitting the name at the first dot (<C>.</C>) or
##    colon (<C>:</C>) and applying the same criteria to derive the group
##    order from the two parts may yield enough information.
##  </Item>
##  <#/GAPDoc>
##
AGR.SizeExceptional2E6:= q -> q^36*(q^12-1)*(q^9+1)*(q^8-1)*(q^6-1)*
                              (q^5+1)*(q^2-1) / Gcd( 3, q+1 );

AGR.SizeExceptionalE:= function( n, q )
    local data;

    if   n = 6 then
      data:= [ 36, [ 12, 9, 8, 6, 5, 2 ], Gcd( 3, q-1 ) ];
    elif n = 7 then
      data:= [ 63, [ 18, 14, 12, 10, 8, 6, 2 ], Gcd( 2, q-1 ) ];
    elif n = 8 then
      data:= [ 120, [ 30, 24, 20, 18, 14, 12, 8, 2 ], 1 ];
    else
      Error( "<n> must be one of 6, 7, 8" );
    fi;

    return q^data[1] * Product( List( data[2], i -> q^i - 1 ) ) / data[3];
end;

AGR.Test.GroupOrders:= function( arg )
    local verbose, formats, maxdeg, HasRemovableOuterBrackets, SizesFromName,
          result, entry, size;

    verbose:= ( Length( arg ) <> 0 and arg[1] = true );

    formats:= [
      [ [ "L", IsDigitChar, "(", IsDigitChar, ")" ],
        l -> Size( PSL( l[2], l[4] ) ) ],
      [ [ "2.L", IsDigitChar, "(", IsDigitChar, ")" ],
        l -> 2 * Size( PSL( l[2], l[4] ) ) ],
      [ [ "S", IsDigitChar, "(", IsDigitChar, ")" ],
        l -> Size( PSp( l[2], l[4] ) ) ],
      [ [ "2.S", IsDigitChar, "(", IsDigitChar, ")" ],
        l -> 2 * Size( PSp( l[2], l[4] ) ) ],
      [ [ "U", IsDigitChar, "(", IsDigitChar, ")" ],
        l -> Size( PSU( l[2], l[4] ) ) ],
      [ [ "E", IsDigitChar, "(", IsDigitChar, ")" ],
        l -> AGR.SizeExceptionalE( l[2], l[4] ) ],
      [ [ "2E6(", IsDigitChar, ")" ],
        l -> AGR.SizeExceptional2E6( l[2] ) ],
    ];

    maxdeg:= AGR.Test.MaxTestDegree;

    HasRemovableOuterBrackets:= function( name )
      local len, open, i;

      len:= Length( name );
      if Length( name ) < 2 or name[1] <> '(' or name[ len ] <> ')' then
        return false;
      fi;
      open:= 0;
      for i in [ 1 .. len-1 ] do
        if name[i] = '(' then
          open:= open + 1;
        elif name[i] = ')' then
          open:= open - 1;
        fi;
        if open = 0 then
          return false;
        fi;
      od;
      return true;
    end;

    SizesFromName:= function( name )
      local result, pair, parse, tbl, tom, data, splitchar, pos,
            name1, name2, size1, size2;

      result:= [];

      # Strip outer brackets.
      while HasRemovableOuterBrackets( name ) do
        name:= name{ [ 2 .. Length( name ) - 1 ] };
      od;

      # Deal with the case of integers.
      if ForAll( name, x -> IsDigitChar( x ) or x in "^()" ) then
        # No other criterion matches with this format, so we return.
        return [ EvalString( name ) ];
      fi;

#T perhaps improve: admit also '+' and '-'
#T if ForAll( name, x -> IsDigitChar( x ) or x in "^()+-" ) then
#T   Print( "name not yet handled: ", name, "\n" );
#T fi;
      for pair in formats do
        parse:= ParseBackwards( name, pair[1] );
        if parse <> fail then
          AddSet( result, pair[2]( parse ) );
        fi;
      od;

      # Try to use the character table information.
      tbl:= CharacterTable( name );
      if tbl <> fail then
        AddSet( result, Size( tbl ) );
      fi;

      # Try to use the table of marks information.
      tom:= TableOfMarks( name );
      if tom <> fail then
        AddSet( result, Size( UnderlyingGroup( tom ) ) );
      fi;

      # Try to use the (locally available) database,
      # but only permutation representations up to degree 'maxdeg'.
      data:= OneAtlasGeneratingSetInfo( name,
                 NrMovedPoints, [ 1 .. maxdeg ],
                 "contents", "local" );
      if data <> fail then
        data:= AtlasGenerators( data );
        if data <> fail then
          AddSet( result, Size( Group( data.generators ) ) );
        fi;
      fi;

      # Try to evaluate the name structure.
      for splitchar in ".:" do
        pos:= Position( name, splitchar );
        while pos <> fail do
          name1:= name{ [ 1 .. pos-1 ] };
          name2:= name{ [ pos+1 .. Length( name ) ] };
          if Length( Positions( name1, '(' ) )
             = Length( Positions( name1, ')' ) ) then
            size1:= SizesFromName( name1 );
            size2:= SizesFromName( name2 );
            if Length( size1 ) = 1 and Length( size2 ) = 1 then
              AddSet( result, size1[1] * size2[1] );
            elif Length( size1 ) > 1 or Length( size2 ) > 1 then
              Print( "#E  AGR.Test.GroupOrders:\n",
                     "#E  group orders: problem with '", name, "'\n" );
              UniteSet( result,
                        Concatenation( List( size1, x -> x * size2 ) ) );
            fi;
          fi;
          pos:= Position( name, splitchar, pos );
        od;
      od;

      return result;
    end;

    result:= true;

    for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
      size:= SizesFromName( entry[1] );
      if 1 < Length( size ) then
        Print( "#E  AGR.Test.GroupOrders:\n",
               "#E  several group orders for '",
               entry[1], "':\n#E  ", size, "\n" );
        result:= false;
      elif not IsBound( entry[3].size ) then
        if Length( size ) = 0 then
          if verbose then
            Print( "#I  AGR.Test.GroupOrders:\n",
                   "#I  group order for '", entry[1], "' unknown\n" );
          fi;
        else
          entry[3].size:= size[1];
          Print( "#I  AGR.Test.GroupOrders:\n",
                 "#I  set group order for '", entry[1], "'\n",
                 "[\"GRS\",[\"", entry[1], "\",", size[1], "]],\n" );
        fi;
      elif Length( size ) = 0 then
        if verbose then
          Print( "#I  AGR.Test.GroupOrders:\n",
                 "#I  cannot verify group order for '", entry[1], "'\n" );
        fi;
      elif size[1] <> entry[3].size then
        Print( "#E  AGR.Test.GroupOrders:\n",
               "#E  wrong group order for '", entry[1], "'\n" );
        result:= false;
      fi;
    od;

    return result;
    end;


#############################################################################
##
#F  AGR.IsFactorFusionWhoseImageHasSameMaxes( <tbl>, <factfus> )
##
##  Let <A>tbl</A> be the character table of a group <M>G</M>, say,
##  and <A>factfus</A> be a factor fusion record from <A>tbl</A>.
##  Let <M>F</M> denote the factor group of <M>G</M> whose character table
##  is given by <A>factfus</A><C>.name</C>.
##  If we can show that the maximal subgroups of <M>F</M> are exactly the
##  images of the maximal subgroups of <M>G</M> under the epimorphism from
##  <M>G</M> to <M>F</M> then this function returns <K>true</K>,
##  otherwise <K>fail</K>.
##  <P/>
##  The function is used to deduce the orders of maximal subgroups from those
##  of suitable factor groups.
##  <P/>
##  The following idea is applied:
##  If <M>K</M> is a normal subgroup in <M>G</M> such that <M>K</M>
##  is contained in the Frattini subgroup <M>\Phi(G)</M> of <M>G</M>
##  (i. e., contained in <E>any</E> maximal subgroup of <M>G</M>)
##  then the maximal subgroups of <M>G</M> are exactly the preimages of the
##  maximal subgroups of <M>G/K</M> under the natural epimorphism.
##  <P/>
##  This situation occurs in the following cases.
##  <List>
##  <Item>
##    If <M>G</M> is perfect then <M>Z(G)</M> is contained in <M>\Phi(G)</M>
##    because <M>G' \cap Z(G) \leq \Phi(G)</M> holds,
##    by <Cite Key="Hup67" SubKey="Kap. III, §3, Satz 3.12)"/>.
##    For example, the orders of the maximal subgroups of <M>3.A_6</M> are
##    the orders of the maximal subgroups of <M>A_6</M>,
##    multiplied by the factor three.
##  </Item>
##  <Item>
##    If <M>G</M> is an upward extension of a perfect group <M>N</M>
##    then <M>Z(N)</M> is contained in <M>\Phi(N)</M>,
##    and since <M>\Phi(N) \leq \Phi(G)</M> holds for any normal subgroup
##    <M>N</M> of <M>G</M>
##    (see <Cite Key="Hup67" SubKey="Kap. III, §3, Hilfssatz 3.3 b)"/>),
##    we get that <M>Z(N)</M> is contained in <M>\Phi(G)</M>.
##    For example the orders of the maximal subgroups of <M>3.A_6.2_1</M> are
##    the orders of the maximal subgroups of <M>A_6.2_1</M>,
##    multiplied by the factor three.
##  </Item>
##  </List>
##
AGR.IsFactorFusionWhoseImageHasSameMaxes:= function( tbl, factfus )
    local ker, nam, subtbl, subfus, subker;

    # Compute the kernel <M>K</M> of the epimorphism.
    ker:= ClassPositionsOfKernel( factfus.map );
    if Length( ker ) = 1 then
      # This is not a factor fusion.
      return fail;
    elif not IsSubset( ClassPositionsOfDerivedSubgroup( tbl ), ker ) then
      # We have no criterion for this case.
      return fail;
    elif IsSubset( ClassPositionsOfCentre( tbl ), ker ) then
      # We have <M>K \leq G' \cap Z(G)</M>,
      # so the maximal subgroups are exactly the preimages of the
      # maximal subgroups in the factor group.
      return true;
    fi;

    # Look for a suitable normal subgroup <M>N</M> of <M>G</M>.
    for nam in NamesOfFusionSources( tbl ) do
      subtbl:= CharacterTable( nam );
      if subtbl <> fail then
        subfus:= GetFusionMap( subtbl, tbl );
        if Size( subtbl ) = Sum( SizesConjugacyClasses( tbl ){
                                   Set( subfus ) } ) and
           IsSubset( subfus, ker ) then
          # <M>N</M> is normal in <M>G</M>, with <M>K \leq N</M>
          subker:= Filtered( [ 1 .. Length( subfus ) ],
                             i -> subfus[i] in ker );
          if IsSubset( ClassPositionsOfDerivedSubgroup( subtbl ),
                     subker ) and
             IsSubset( ClassPositionsOfCentre( subtbl ), subker ) then
            # We have <M>K \leq N' \cap Z(N)</M>.
            return true;
          fi;
        fi;
      fi;
    od;

    return fail;
    end;


#############################################################################
##
#F  AGR.Test.MaxesOrders( [<tocid>,[ <entry>][,][<verbose>] )
##
##  <#GAPDoc Label="test:AGR.Test.MaxesOrders">
##  <Mark><C>AGR.Test.MaxesOrders( [</C><M>tocid</M><C>] )</C></Mark>
##  <Item>
##    checks whether the orders of maximal subgroups stored in the component
##    <C>GAPnames</C> of <Ref Var="AtlasOfGroupRepresentationsInfo"/>
##    coincide with the orders computed from the restriction of an &ATLAS;
##    permutation representation of degree up to
##    <C>AGR.Test.MaxTestDegree</C>
##    (using a straight line program that belongs to <M>tocid</M>),
##    from the character table, or the table of marks with the given name,
##    or from the information about maximal subgroups of the factor group
##    modulo a normal subgroup that is contained in the Frattini subgroup.
##  </Item>
##  <#/GAPDoc>
##
AGR.Test.MaxesOrders:= function( arg )
    local verbose, tocid, result, toc, extend, maxdeg, maxmax,
          MaxesInfoForName, entry, info, size, filt;

    verbose:= ( Length( arg ) <> 0 and arg[ Length( arg ) ] = true );
    tocid:= First( arg, IsString );
    if tocid = fail then
      tocid:= "core";
    fi;
    result:= true;
    toc:= AGR.TablesOfContents( [ tocid, "local" ] );
    if toc = fail then
      return result;
    fi;
    toc:= toc[1];

    extend:= ( ValueOption( "TryToExtendData" ) = true );

    maxdeg:= AGR.Test.MaxTestDegree;
    maxmax:= AGR.Test.HardCases.MaxNumberMaxes;

    MaxesInfoForName:= function( name )
      local result, nrmaxes, tbl, oneresult, i,
            subtbl, tom, std, g, prg, gens, factfus, recurs, good;

      result:= [];
      nrmaxes:= [];

      # Try to use the character table information.
      tbl:= CharacterTable( name );
      if tbl <> fail then
        if HasMaxes( tbl ) then
          AddSet( nrmaxes, Length( Maxes( tbl ) ) );
          AddSet( result, List( Maxes( tbl ),
                                 nam -> Size( CharacterTable( nam ) ) ) );
        else
          # Try whether individual maxes are supported.
          oneresult:= [];
          if tbl <> fail then
            for i in [ 1 .. maxmax ] do
              subtbl:= CharacterTable( Concatenation( Identifier( tbl ), "M",
                                                      String( i ) ) );
              if subtbl <> fail then
                oneresult[i]:= Size( subtbl );
              fi;
            od;
          fi;
          if not IsEmpty( oneresult ) then
            AddSet( result, oneresult );
          fi;
        fi;
      fi;

      # Try to use the table of marks information.
# more tests: how to identify FusionsToLibTom( tom )?
      tom:= TableOfMarks( name );
      if tom <> fail then
        AddSet( nrmaxes, Length( MaximalSubgroupsTom( tom )[1] ) );
        AddSet( result, Reversed( SortedList( OrdersTom( tom ){
                             MaximalSubgroupsTom( tom )[1] } ) ) );
      fi;

      # Try to use the AtlasRep database.
      for std in [ 1 .. AGR.Test.HardCases.MaxNumberStd ] do
        g:= AtlasGroup( name, std, "contents", "local" );
        if ( g <> fail ) and
           ( ( HasSize( g ) and Size( g ) < 10^7 ) or
             ( IsPermGroup( g ) and NrMovedPoints( g ) <= maxdeg ) ) then
          oneresult:= [];
          for i in [ 1 .. maxmax ] do
            if extend then
              prg:= AtlasProgram( name, std, "maxes", i, "contents", "local" );
            else
              prg:= AtlasProgram( name, std, "maxes", i,
                                  "contents", [ tocid, "local" ] );
            fi;
            if prg <> fail then
              gens:= ResultOfStraightLineProgram( prg.program,
                                                  GeneratorsOfGroup( g ) );
              if verbose then
                Print( "#I  AGR.Test.MaxesOrders:\n",
                       "#I  computing max. ", i, " for ", name, "\n" );
              fi;
              oneresult[i]:= Size( SubgroupNC( g, gens ) );
            fi;
          od;
          if not IsEmpty( oneresult ) then
            AddSet( result, oneresult );
          fi;
        fi;
      od;

      # Try to deduce the orders of maximal subgroups from those of factors.
      if tbl <> fail then
        for factfus in ComputedClassFusions( tbl ) do
          if AGR.IsFactorFusionWhoseImageHasSameMaxes( tbl, factfus ) = true
             then
            recurs:= MaxesInfoForName( factfus.name );
            UniteSet( nrmaxes, recurs.nrmaxes );
            UniteSet( result,
              recurs.maxesorders * Sum( SizesConjugacyClasses( tbl ){
                  ClassPositionsOfKernel( factfus.map ) } ) );
          fi;
        od;
      fi;

      # Compact the partial results.
      good:= true;
      for oneresult in result{ [ 2 .. Length( result ) ] } do
        for i in [ 1 .. Length( oneresult ) ] do
          if   IsBound( result[1][i] ) then
            if IsBound( oneresult[i] ) then
              if result[1][i] <> oneresult[i] then
                good:= false;
              fi;
            fi;
          elif IsBound( oneresult[i] ) then
            result[1][i]:= oneresult[i];
          fi;
        od;
      od;
      if good and not IsEmpty( result ) then
        result:= [ result[1] ];
      fi;

      return rec( maxesorders:= result,
                  nrmaxes:= Set( nrmaxes ) );
    end;

    if Length( arg ) = 0 or
       ForAll( arg, x -> IsString( x ) or IsBool( x ) ) then
      for entry in AtlasOfGroupRepresentationsInfo.GAPnames do
        result:= AGR.Test.MaxesOrders( tocid, entry, verbose ) and result;
      od;
    else
      entry:= First( arg, x -> not IsBool( x ) and not IsString( x ) );
      info:= MaxesInfoForName( entry[1] );

      if not IsBound( entry[3].nrMaxes ) then
        if Length( info.nrmaxes ) = 1 then
          Print( "#I  AGR.Test.MaxesOrders:\n",
                 "#I  set maxes number for '", entry[1], "':\n",
                 "[\"MXN\",[\"", entry[1], "\",", info.nrmaxes[1], "]],\n" );
        fi;
      elif Length( info.nrmaxes ) <> 1 then
        if verbose then
          Print( "#I  AGR.Test.MaxesOrders:\n",
                 "#I  cannot verify stored maxes number for '", entry[1],
                 "'\n" );
        fi;
      fi;

      size:= info.maxesorders;
      if 1 < Length( size ) then
        Print( "#E  AGR.Test.MaxesOrders:\n",
               "#E  several maxes orders for '", entry[1], "':\n",
               "#E  ", size, "\n" );
        result:= false;
      elif not IsBound( entry[3].sizesMaxes )
           or IsEmpty( entry[3].sizesMaxes ) then
        # No maxes orders are stored yet.
        if Length( size ) = 0 then
          if IsBound( toc.( entry[2] ) ) and
             IsBound( toc.( entry[2] ).maxes ) and
             not IsEmpty( toc.( entry[2] ).maxes ) then
            # We have at least one straight line program but no repres.
            Print( "#I  AGR.Test.MaxesOrders:\n",
                   "#I  maxes orders for '", entry[1],
                   "' unknown (but slps available)\n" );
          elif verbose then
            # We have no information about maximal subgroups.
            Print( "#I  AGR.Test.MaxesOrders:\n",
                   "#I  maxes orders for '", entry[1], "' unknown\n" );
          fi;
        else
          if IsBound( entry[3].size ) then
            if entry[3].size in size[1] then
              Print( "#E  AGR.Test.MaxesOrders:\n",
                     "#E  group order in maxes orders list for '", entry[1],
                     "'\n" );
              result:= false;
--> --------------------

--> maximum size reached

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

[ Dauer der Verarbeitung: 0.73 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


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