Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/GAP/pkg/atlasrep/gap/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 3.0.2024 mit Größe 161 kB image not shown  

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.29 Sekunden  (vorverarbeitet)  ]