#############################################################################
##
#W  types.g              GAP 4 package `atlasrep'               Thomas Breuer
##
#H  @(#)$Id: types.g,v 1.32 2004/04/07 16:21:27 gap Exp $
##
#Y  Copyright (C)  2001,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
##
##  This file contains implementations of the actual data types used in the
##  {\ATLAS} of Group Representations.
##
Revision.( "atlasrep/gap/types_g" ) :=
    "@(#)$Id: types.g,v 1.32 2004/04/07 16:21:27 gap Exp $";


#############################################################################
##
#V  AtlasOfGroupRepresentationsInfo
##
BindGlobal( "AtlasOfGroupRepresentationsInfo", rec(

    servers := [
                 [ "for.mat.bham.ac.uk", "v2.0/", "atlasftp", "atlasftp" ],
               ],

    remote := true,

    compress := false,

    ls := ( Filename( DirectoriesSystemPrograms(), "ls" ) <> fail ),

    dirnames := [
                  "alt",
                  "clas",
                  "exc",
                  "lin",
                  "misc",
                  "spor",
                ],

    GAPnames := [],

    groupnames := [],

    fieldinfo := [],

    private := [],

    markprivate := "*",

    TableOfContents := rec( remote := rec(),
                            types := rec( rep   := [],
                                          slp   := [],
                                          cache := [] ) )

    ) );


#############################################################################
##
#D  Permutation representations
##
AGRDeclareDataType( "rep", "perm", rec(

    # `<groupname>G<i>-p<n><id>B<m>.m<nr>'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                          [ "p", IsDigitChar, IsLowerAlphaOrDigitChar,
                            "B", IsDigitChar, ".m", IsDigitChar ] ],
                        [ ParseBackwards, ParseForwards ] ],

    # `[ <i>, <n>, <id>, <m>, <filenames> ]'
    AddFileInfo := function( list, entry, name )
      local known;
      if 0 < entry[5] then
        known:= First( list, x -> x{ [ 1 .. 4 ] } = entry{ [3, 5, 6, 8 ] } );
        if known = fail then
          known:= entry{ [ 3, 5, 6, 8 ] };
          Add( known, [] );
          Add( list, known );
        fi;
        known[5][ entry[10] ]:= name;
        return true;
      fi;
      return false;
    end,

    DisplayOverviewInfo := [ "#", "r", function( tocs, groupname )
      # Put *all* types of representations together, in particular
      # assume that the functions for the other types are trivial!
      local no, private, j, toc, record, new, type;

      no:= 0;
      private:= false;
      for j in [ 1 .. Length( tocs ) ] do
        toc:= tocs[j];
        if IsBound( toc.( groupname ) ) then
          record:= toc.( groupname );
          new:= 0;
          for type in AGRDataTypes( "rep" ) do
            if IsBound( record.( type[1] ) ) then
              new:= new + Length( record.( type[1] ) );
            fi;
          od;
          if 1 < j and 0 < new then
            private:= true;
          fi;
          no:= no + new;
        fi;
      od;
      return [ String( no ), private ];
    end ],

    AccessGroupCondition := function( entry, cond )
      return  AGRCheckOneCondition( IsPermGroup, x -> x = true, cond )
          and AGRCheckOneCondition( IsPermGroup, cond )
          and AGRCheckOneCondition( IsMatrixGroup, x -> x = false, cond )
          and AGRCheckOneCondition( NrMovedPoints, x -> entry[2] = x, cond )
          and IsEmpty( cond );
    end,

    DisplayGroup := x -> Concatenation( "G <= Sym(",String(x[2]),x[3],")" ),

    TestFileHeaders := function( tocid, groupname, entry )
      local name, filename, file, line;

      if tocid = "local" then
        tocid:= "datagens";
      fi;

      # Each generator is stored in a file of its own.
      for name in entry[ Length( entry ) ] do

        # Read the first line of the file.
        filename:= FilenameAtlas( tocid, groupname, name );
        if filename = fail then
          return Concatenation( "filename `", name, "' not found" );
        fi;
        file:= InputTextFile( filename );
        if file = fail then
          return Concatenation( "cannot create input stream for file `",
                     filename,"'" );
        fi;
        InfoRead1( "#I  reading `",filename,"' started\n" );
        line:= ReadLine( file );
        if line = fail then
          CloseStream( file );
          return Concatenation( "no first line in file `",filename,"'" );
        fi;
        CloseStream( file );
        InfoRead1( "#I  reading `",filename,"' done\n" );

        # The header must consist of four nonnegative integers.
        line:= CMeatAxeFileHeaderInfo( line );
        if line = fail then
          return Concatenation( "illegal header of file `", filename,"'" );
        fi;

        # Start the specific tests for permutations.
        # Check mode, number of permutations, and degree.
        if   line[1] <> 12 then
          return Concatenation( "mode of file `", name,
                     "' differs from 12" );
        elif line[4] <> 1 then
          return Concatenation(
                "more than one permutation in file `", name, "'" );
        elif line[3] <> entry[2] then
          return Concatenation( "perm. degree in file `",
                     name, "' is ", String( line[3] ) );
        fi;

      od;
      return true;
    end,

    TestFiles := AGRTestFilesMTX,

    # Permutation representations are sorted according to
    # degree and identification string.
    SortTOCEntries := entry -> entry{ [ 2, 3 ] },

    PostprocessFileInfo := function( toc, record )
      local list, i;
      list:= record.perm;
      for i in [ 1 .. Length( list ) ] do
        if not IsDenseList( list[i][5] ) then
#T better check whether the number of generators equals the number of
#T standard generators!
          Info( InfoAtlasRep, 1, "not all generators for ", list[i][5] );
          Unbind( list[i] );
        fi;
      od;
      if not IsDenseList( list ) then
        record.perm:= Compacted( list );
      fi;
    end,

    # We store the stem of the filename and the number of generators.
    TOCEntryString := function( typename, entry )
      return Concatenation( [
          "AGRTOC(\"", typename, "\",\"",
          entry[5][1]{ [ 1 .. Length( entry[5][1] ) - 1 ] },
          "\",",
          String( Length( entry[5] ) ),
          ");\n" ] );
    end ) );


#############################################################################
##
#D  Matrix representations over finite fields
##
AGRDeclareDataType( "rep", "matff",   rec(

    # `<groupname>G<i>-f<q>r<dim><id>B<m>.m<nr>'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                          [ "f", IsDigitChar, "r", IsDigitChar,
                            IsLowerAlphaOrDigitChar,
                            "B", IsDigitChar, ".m", IsDigitChar ] ],
                        [ ParseBackwards, ParseForwards ] ],

    # `[ <i>, <q>, <dim>, <id>, <m>, <filenames> ]'
    AddFileInfo := function( list, entry, name )
      local known;
      if IsPrimePowerInt( entry[5] ) and 0 < entry[7] then
        known:= First( list, x -> x{ [ 1 .. 5 ] }
                                  = entry{ [ 3, 5, 7, 8, 10 ] } );
        if known = fail then
          known:= entry{ [ 3, 5, 7, 8, 10 ] };
          Add( known, [] );
          Add( list, known );
        fi;
        known[6][ entry[12] ]:= name;
        return true;
      fi;
      return false;
    end,

    AccessGroupCondition := function( entry, cond )
      return  AGRCheckOneCondition( IsMatrixGroup, x -> x = true, cond )
          and AGRCheckOneCondition( IsMatrixGroup, cond )
          and AGRCheckOneCondition( IsPermGroup, x -> x = false, cond )
          and AGRCheckOneCondition( Characteristic,
                                    p -> IsPosInt( p )
                                     and IsPrimeInt( p )
                                     and entry[2] mod p = 0,
                                    cond )
          and AGRCheckOneCondition( Dimension, x -> x = entry[3], cond )
          and AGRCheckOneCondition( Ring, R -> IsField( R ) and IsFinite( R )
                         and entry[2] mod Characteristic( R ) = 0
                         and DegreeOverPrimeField( R )
                            mod LogInt( entry[2], Characteristic( R ) ) = 0,
                         cond )
          and IsEmpty( cond );
    end,

    DisplayGroup := x -> Concatenation( "G <= GL(",String(x[3]),x[4],",",
                                        String(x[2]),")" ),

    TestFileHeaders := function( tocid, groupname, entry )
      local name, filename, file, line;

      if tocid = "local" then
        tocid:= "datagens";
      fi;

      # Each generator is stored in a file of its own.
      for name in entry[ Length( entry ) ] do

        # Read the first line of the file.
        filename:= FilenameAtlas( tocid, groupname, name );
        if filename = fail then
          return Concatenation( "filename `", name, "' not found" );
        fi;
        file:= InputTextFile( filename );
        if file = fail then
          return Concatenation( "cannot create input stream for file `",
                     filename,"'" );
        fi;
        InfoRead1( "#I  reading `",filename,"' started\n" );
        line:= ReadLine( file );
        if line = fail then
          CloseStream( file );
          return Concatenation( "no first line in file `",filename,"'" );
        fi;
        CloseStream( file );
        InfoRead1( "#I  reading `",filename,"' done\n" );

        # The header must consist of four nonnegative integers.
        line:= CMeatAxeFileHeaderInfo( line );
        if line = fail then
          return Concatenation( "illegal header of file `", filename,"'" );
        fi;

        # Start the specific tests for matrices over finite fields.
        # Check mode, field size, and dimension.
        if   6 < line[1] then
          return Concatenation( "mode of file `", name,
                     "' is larger than 6" );
        elif line[2] <> entry[2] then
          return Concatenation( "file `", name,
                     "': field is of size ", String( line[2] ) );
        elif line[3] <> entry[3] then
          return Concatenation( "file `", name,
                     "': matrix dimension is ", String( line[3] ) );
        elif line[3] <> line[4] then
          return Concatenation( "file `", name,
                     "': matrix is not square" );
        fi;

      od;
      return true;
    end,

    TestFiles := AGRTestFilesMTX,

    # Matrix representations over finite fields are sorted according to
    # characteristic, dimension, and identification string.
    SortTOCEntries := entry -> entry{ [ 2 .. 4 ] },

    PostprocessFileInfo := function( toc, record )
      local list, i;
      list:= record.matff;
      for i in [ 1 .. Length( list ) ] do
        if not IsDenseList( list[i][6] ) then
#T better check whether the number of generators equals the number of
#T standard generators!
          Info( InfoAtlasRep, 1, "not all generators for ", list[i][6] );
          Unbind( list[i] );
        fi;
      od;
      if not IsDenseList( list ) then
        record.matff:= Compacted( list );
      fi;
    end,

    # We store the stem of the filename and the number of generators.
    TOCEntryString := function( typename, entry )
      return Concatenation( [
          "AGRTOC(\"", typename, "\",\"",
          entry[6][1]{ [ 1 .. Length( entry[6][1] ) - 1 ] },
          "\",",
          String( Length( entry[6] ) ),
          ");\n" ] );
    end ) );


#############################################################################
##
#D  Matrix representations over the integers
##
AGRDeclareDataType( "rep", "matint",  rec(

    # `<groupname>G<i>-Zr<dim><id>B<m>.g'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                        [ "Zr", IsDigitChar, IsLowerAlphaOrDigitChar,
                          "B", IsDigitChar, ".g" ] ],
                        [ ParseBackwards, ParseForwards ] ],

    # `[ <i>, <dim>, <id>, <m>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      if 0 < entry[5] then
        Add( list, Concatenation( entry{ [ 3, 5, 6, 8 ] }, [ name ] ) );
        return true;
      fi;
      return false;
    end,

    AccessGroupCondition := function( entry, cond )
      return  AGRCheckOneCondition( IsMatrixGroup, x -> x = true, cond )
          and AGRCheckOneCondition( IsMatrixGroup, cond )
          and AGRCheckOneCondition( IsPermGroup, x -> x = false, cond )
          and AGRCheckOneCondition( Characteristic, IsZeroCyc, cond )
          and AGRCheckOneCondition( Dimension, x -> x = entry[2], cond )
          and AGRCheckOneCondition( Ring, R -> IsRing( R )
                  and IsCyclotomicCollection( R ), cond )
          and IsEmpty( cond );
    end,

    TestFileHeaders := function( tocid, groupname, entry )
      return AGRTestFileHeadersDefault( tocid, groupname, entry, entry[2],
               function( entry, mats, filename )
                 if not ForAll( mats, mat -> ForAll( mat,
                                 row -> ForAll( row, IsInt ) ) ) then
                   return Concatenation( "matrices in `",filename,
                              "' are not over the integers" );
                 fi;
                 return true;
               end );
    end,

    DisplayGroup := x -> Concatenation( "G <= GL(",String(x[2]),x[3],",Z)" ),

    # Matrix representations over the integers are sorted according to
    # dimension and identification string.
    SortTOCEntries := entry -> entry{ [ 2, 3 ] }
    ) );


#############################################################################
##
#D  Matrix representations over algebraic number fields
##
AGRDeclareDataType( "rep", "matalg",  rec(

    # `<groupname>G<i>-Ar<dim><id>B<m>.g'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                        [ "Ar", IsDigitChar, IsLowerAlphaOrDigitChar,
                          "B", IsDigitChar, ".g" ] ],
                        [ ParseBackwards, ParseForwards ] ],

    # `[ <i>, <dim>, <id>, <m>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      if 0 < entry[5] then
        Add( list, Concatenation( entry{ [ 3, 5, 6, 8 ] }, [ name ] ) );
        return true;
      fi;
      return false;
    end,

    AccessGroupCondition := function( entry, cond )
      local checkfun;

      checkfun:= function( R )
        local info, format, groupname, dirname;
        if not IsRing( R ) or not IsCyclotomicCollection( R ) then
          return false;
        elif IsIdenticalObj( R, Cyclotomics ) then
          return true;
        fi;
        info:= First( AtlasOfGroupRepresentationsInfo.fieldinfo,
                      x -> x[1] = entry[5] );
        if info = fail then
          # Access the generators explicitly.
#T (This can be very expensive ...)
          format:= First( AGRDataTypes( "rep" ),
                          x -> x[1] = "matalg" )[2].FilenameFormat;
          groupname:= AGRParseFilenameFormat( entry[5], format )[1];
          info:= FilenameAtlas( "datagens", groupname, entry[5] );
          if info = fail then
            for dirname in List( AtlasOfGroupRepresentationsInfo.private,
                                 x -> x[1] ) do
              info:= FilenameAtlas( dirname, groupname, entry[5] );
              if info <> fail then
                break;
              fi;
            od;
          fi;
          if info = fail then
            Error( "<entry> is not registered" );
          fi;
          return IsSubset( R, AtlasStringOfFieldOfMatrixEntries( info )[1] );
        else
          return IsSubset( R, info[3] );
        fi;
      end;

      return  AGRCheckOneCondition( IsMatrixGroup, x -> x = true, cond )
          and AGRCheckOneCondition( IsMatrixGroup, cond )
          and AGRCheckOneCondition( IsPermGroup, x -> x = false, cond )
          and AGRCheckOneCondition( Characteristic, IsZeroCyc, cond )
          and AGRCheckOneCondition( Dimension, x -> x = entry[2], cond )
          and AGRCheckOneCondition( Ring, checkfun, cond )
          and IsEmpty( cond );
    end,

    TestFileHeaders := function( tocid, groupname, entry )
      return AGRTestFileHeadersDefault( tocid, groupname, entry, entry[2],
               function( entry, mats, filename )
                 local info;

                 if not IsCyclotomicCollCollColl( mats ) then
                   return Concatenation( "matrices in `",filename,
                              "' are not over cyclotomics" );
                 elif ForAll( Flat( mats ), IsInt ) then
                   return Concatenation( "matrices in `",filename,
                              "' are over the integers" );
                 fi;
                 filename:= filename{ [ 1 .. Position( filename, '.' )-1 ] };
                 info:= First( AtlasOfGroupRepresentationsInfo.fieldinfo,
                               triple -> triple[1] = filename );
                 if info = fail then
                   return Concatenation( "field info for `",filename,
                              "' missing" );
                 elif Field( Rationals, Flat( mats ) ) <> info[3] then
                   return Concatenation( "field info for `",filename,
                              "' should be ",
                              String( Field( Rationals, Flat( mats ) ) ) );
                 fi;
                 return true;
               end );
    end,

    DisplayGroup := function( x )
      local fieldstring;
      fieldstring:= x[5]{ [ 1 .. Length( x[5] )-2 ] };
      fieldstring:= First( AtlasOfGroupRepresentationsInfo.fieldinfo,
                           p -> p[1] = fieldstring );
      if fieldstring = fail then
        fieldstring:= "C";
      else
        fieldstring:= fieldstring[2];
      fi;
      return Concatenation( "G <= GL(",String(x[2]),x[3],",",fieldstring,
                            ")" );
    end,

    # Matrix representations over algebraic extension fields are sorted
    # according to dimension and identification string.
    SortTOCEntries := entry -> entry{ [ 2, 3 ] }
    ) );


#############################################################################
##
#D  Matrix representations over residue class rings
##
AGRDeclareDataType( "rep", "matmodn", rec(

    # `<groupname>G<i>-Z<n>r<dim><id>B<m>.g'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                          [ "Z", IsDigitChar, "r", IsDigitChar,
                            IsLowerAlphaOrDigitChar,
                            "B", IsDigitChar, ".g" ] ],
                        [ ParseBackwards, ParseForwards ] ],

    # `[ <i>, <n>, <dim>, <id>, <m>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      if 0 < entry[5] and 0 < entry[7] then
        Add( list, Concatenation( entry{ [ 3, 5, 7, 8, 10 ] }, [ name ] ) );
        return true;
      fi;
      return false;
    end,

    AccessGroupCondition := function( entry, cond )
      return  AGRCheckOneCondition( IsMatrixGroup, x -> x = true, cond )
          and AGRCheckOneCondition( IsMatrixGroup, cond )
          and AGRCheckOneCondition( IsPermGroup, x -> x = false, cond )
          and AGRCheckOneCondition( Characteristic, x -> x = fail, cond )
          and AGRCheckOneCondition( Dimension, x -> x = entry[3], cond )
          and AGRCheckOneCondition( Ring, R -> IsRing( R )
                  and IsZmodnZObjNonprimeCollection( R )
                  and ModulusOfZmodnZObj( One( R ) ) = entry[2], cond )
          and IsEmpty( cond );
    end,

    DisplayGroup := x -> Concatenation( "G <= GL(",String(x[3]),x[4],",Z/",
                                        String(x[2]),"Z)" ),

    TestFileHeaders := function( tocid, groupname, entry )
      return AGRTestFileHeadersDefault( tocid, groupname, entry, entry[3],
               function( entry, mats, filename )
                 if   not IsZmodnZObjNonprimeCollCollColl( mats ) then
                   return Concatenation( "matrices in `", filename,
                              "' are not over a residue class ring" );
                 elif ModulusOfZmodnZObj( mats[1][1][1] ) <> entry[2] then
                   return Concatenation( "matrices in `", filename,
                              "' are not over Z/", entry[2], "Z" );
                 fi;
                 return true;
               end );
    end,

    # Matrix representations over residue class rings are sorted according
    # to modulus, dimension, and identification string.
    SortTOCEntries := entry -> entry{ [ 2 .. 4 ] }
    ) );


#############################################################################
##
#D  Quaternionic matrix representations
##
AGRDeclareDataType( "rep", "quat",  rec(

    # `<groupname>G<i>-Hr<dim><id>B<m>.g'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                          [ "Hr", IsDigitChar, IsLowerAlphaOrDigitChar,
                            "B", IsDigitChar, ".g" ] ],
                        [ ParseBackwards, ParseForwards ] ],

    # `[ <i>, <dim>, <id>, <m>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      if 0 < entry[5] then
        Add( list, Concatenation( entry{ [ 3, 5, 6, 8 ] }, [ name ] ) );
        return true;
      fi;
      return false;
    end,

    AccessGroupCondition := function( entry, cond )
      return  AGRCheckOneCondition( IsMatrixGroup, x -> x = true, cond )
          and AGRCheckOneCondition( IsMatrixGroup, cond )
          and AGRCheckOneCondition( IsPermGroup, x -> x = false, cond )
          and AGRCheckOneCondition( Characteristic, IsZeroCyc, cond )
          and AGRCheckOneCondition( Dimension, x -> x = entry[2], cond )
          and AGRCheckOneCondition( Ring, R -> IsRing( R ) and
                  IsQuaternionCollection( R ), cond )
#T check the embedding of the LeftActingDomain!!
          and IsEmpty( cond );
    end,

    TestFileHeaders := function( tocid, groupname, entry )
      return AGRTestFileHeadersDefault( tocid, groupname, entry, entry[2],
               function( entry, mats, filename )
                 local info;

                 if not ForAll( mats, IsQuaternionCollColl ) then
                   return Concatenation( "matrices in `",filename,
                              "' are not over the quaternions" );
                 fi;
                 filename:= filename{ [ 1 .. Position( filename, '.' )-1 ] };
                 info:= First( AtlasOfGroupRepresentationsInfo.fieldinfo,
                               triple -> triple[1] = filename );
                 if info = fail then
                   return Concatenation( "field info for `",filename,
                              "' missing" );
                 elif Field( Flat( List( Flat( mats ), ExtRepOfObj ) ) )
                      <> EvalString( Concatenation( "Field",
                             info[2]{ [ Position( info[2], '(' ) ..
                                      Length( info[2] ) ] } ) ) then
                   return Concatenation( "field info for `", filename,
                              "' should involve ",
                              Field( Flat( List( Flat( mats ),
                                                 ExtRepOfObj ) ) ) );
                 fi;
                 return true;
               end );
    end,

    DisplayGroup := function( x )
      local fieldstring;
      fieldstring:= x[5]{ [ 1 .. Length( x[5] )-2 ] };
      fieldstring:= First( AtlasOfGroupRepresentationsInfo.fieldinfo,
                           p -> p[1] = fieldstring );
      if fieldstring = fail then
        fieldstring:= "H";
#T not actually correct, we may need an extension ...
      else
        fieldstring:= fieldstring[2];
      fi;
      return Concatenation( "G <= GL(",String(x[2]),x[3],",",fieldstring,
                            ")" );
    end,

    # Matrix representations over the quaternions are sorted according to
    # dimension and identification string.
    SortTOCEntries := entry -> entry{ [ 2, 3 ] }
    ) );


#############################################################################
##
#D  Straight line programs for generators of maximal subgroups
##
AGRDeclareDataType( "slp", "maxes", rec(

    # `<groupname>G<i>-max<k>W<n>'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                          [ "max", IsDigitChar, "W", IsDigitChar ] ],
                        [ ParseBackwards, ParseForwards ] ],

    # `[ <i>, <k>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      if 0 < entry[5] then
        Add( list, Concatenation( entry{ [ 3, 5 ] }, [ name ] ) );
        return true;
      fi;
      return false;
    end,

    DisplayOverviewInfo := [ "maxes", "r", function( tocs, groupname )
    local value, private, j, toc, record, new, type;

    value:= [];
    private:= false;
    for j in [ 1 .. Length( tocs ) ] do
      toc:= tocs[j];
      if IsBound( toc.( groupname ) ) then
        record:= toc.( groupname );
        if IsBound( record.maxes ) then
          new:= List( record.maxes, x -> x[2] );
          if 1 < j and not IsEmpty( new ) then
            private:= true;
          fi;
          UniteSet( value, new );
        fi;
      fi;
    od;
    if IsEmpty( value ) then
      value:= "";
    elif 2 < Length( value ) and IsRange( value )
                           and value[2] = value[1] + 1 then
      value:= Concatenation( String( value[1] ),
                             "..",
                             String( value[ Length( value ) ] ) );
    else
      value:= Concatenation( List( value,
                                   x -> Concatenation( String(x), "," ) ) );
      Unbind( value[ Length( value ) ] );
    fi;
    return [ value, private ];
    end ],

    DisplaySLP := function( tocs, name, std, stdavail )
      local maxes, maxstd, private, j, toc, record, i, result, line;

      maxes   := [];
      maxstd  := [];
      private := "";

      for j in [ 1 .. Length( tocs ) ] do
        toc:= tocs[j];
        if IsBound( toc.( name ) ) then
          record:= toc.( name );
          if IsBound( record.maxes ) then
            for i in record.maxes do
              if std = true or i[1] in std then
                if 1 < j then
                  private:= AtlasOfGroupRepresentationsInfo.markprivate;
                fi;
                if i[2] in maxes then
                  AddSet( maxstd[ Position( maxes, i[2] ) ], i[1] );
                else
                  Add( maxes, i[2] );
                  Add( maxstd, [ i[1] ] );
                fi;
              fi;
            od;
          fi;
        fi;
      od;
      SortParallel( maxes, maxstd );
      if 2 < Length( maxes ) then
        ConvertToRangeRep( maxes );
      fi;

      result:= [];
      if not IsEmpty( maxes ) then
        line:= "available maxes of G:";
        if Length( stdavail ) <= 1 then
          Append( line, "  " );
          Append( line, String( maxes ) );
          Append( line, private );
          Append( line, "\n" );
          Add( result, line );
        else
          Append( line, "\n" );
          Add( result, line );
          for i in Union( maxstd ) do
            Add( result, Concatenation( "  ",
                String( maxes{ Filtered( [ 1 .. Length( maxes ) ],
                                         x -> i in maxstd[x] ) } ),
                private,
                "    (w.r.t. std. generators ", String( i ), ")\n" ) );
          od;
        fi;
      fi;
      return result;
    end,

    # entry: `[ <std>, <maxnr>, <file> ]',
    # conditions: `[ "maxes", <maxnr> ]'
    AccessSLP := function( record, std, conditions )
      local entry;
      if conditions[1] = "maxes" and IsBound( record.maxes ) then
        for entry in record.maxes do
          if     ( std = true or entry[1] in std )
             and entry[2] = conditions[2] then
            return entry{ [ 3, 1 ] };
          fi;
        od;
      fi;
      return fail;
    end,

    # Maxes are sorted according to their natural position.
    SortTOCEntries := entry -> entry[2],

    # In addition to the tests in `AGRTestWordsDefault',
    # compute the images in a representation if available,
    # and compare the group order with that stored in the
    # {\GAP} Character Table Library (if available).
    TestWords:= function( tocid, name, file )
        local filename, prog, prg, gens, gapname, tbl, pos, pos2, maxnr,
              subname, subtbl, std, grp, size;

        # Read the program.
        if tocid = "local" then
          tocid:= "dataword";
        fi;
        filename:= FilenameAtlas( tocid, name, file );
        if filename = fail then
          Print( "#E  no filename `", file, "' for `", name, "'\n" );
          return false;
        fi;
        prog:= ScanStraightLineProgram( filename );

        # Check consistency.
        if prog = fail or not IsInternallyConsistent( prog.program ) then
          Print( "#E  program `", file, "' not internally consistent\n" );
          return false;
        fi;
        prg:= prog.program;

        # Create a list of trivial generators.
        gens:= ListWithIdenticalEntries(
                   NrInputsOfStraightLineProgram( prg ), () );

        # Run the program.
        gens:= ResultOfStraightLineProgram( prg, gens );

        # Identify the group order in the {\GAP} Character Table Library.
        gapname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                         pair -> name = pair[2] );
        if gapname = fail then
          Print( "#E  problem: no GAP name for `", name, "'\n" );
          return false;
        fi;
        gapname:= gapname[1];
        tbl:= CharacterTable( gapname );
        if not IsCharacterTable( tbl ) then
          Print( "#I  no character table for `", gapname,
                 "', no check for `", file, "'\n" );
          return true;
        fi;

        # Compute the position in the `Maxes' list.
        pos:= PositionSublist( filename, "-max" );
        pos2:= pos + 4;
        while filename[ pos2 ] <> 'W' do
          pos2:= pos2 + 1;
        od;
        maxnr:= Int( filename{ [ pos+4 .. pos2-1 ] } );
        if HasMaxes( tbl ) then
          if Length( Maxes( tbl ) ) < maxnr then
            Print( "#E  program `", file,
                   "' contradicts `Maxes( ", tbl, " )'\n" );
            return false;
          fi;
          subname:= Maxes( tbl )[ maxnr ];
        else
          subname:= Concatenation( Identifier( tbl ), "M", String( maxnr ) );
        fi;
        subtbl:= CharacterTable( subname );
        if not IsCharacterTable( subtbl ) then
          Print( "#I  no character table for `", subname,
                 "', no check for `", file, "'\n" );
          return true;
        fi;

        # Compute the standardization.
        pos2:= pos - 1;
        while filename[ pos2 ] <> 'G' do
          pos2:= pos2-1;
        od;
        std:= Int( filename{ [ pos2+1 .. pos-1 ] } );

        # Get a representation if available, and map the generators.
        gens:= OneAtlasGeneratingSet( gapname, std, IsPermGroup, true,
                   NrMovedPoints, [ 2 .. 10^5 ] );
        if gens = fail then
          Print( "#I  no perm. repres. for `", gapname,
                 "', no check for `", file, "'\n" );
        else
          grp:= Group( gens.generators );
          SetSize( grp, Size( tbl ) );
          gens:= ResultOfStraightLineProgram( prg, gens.generators );
          size:= Size( SubgroupNC( grp, gens ) );
          if size <> Size( subtbl ) then
            Print( "#E  program `", file, "' for group of order ", size,
                   " not `", Identifier( subtbl ), "'\n" );
            return false;
          fi;
        fi;

        # No more tests are available.
        return true;
    end

    ) );


#############################################################################
##
#D  Straight line programs for class representatives
##
AGRDeclareDataType( "slp", "classes", rec(

    # `<groupname>G<i>-cclsW<n>'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                          [ "cclsW", IsDigitChar ] ],
                        [ ParseBackwards, ParseForwards ] ],

    # `[ <i>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      Add( list, Concatenation( entry{ [ 3 ] }, [ name ] ) );
      return true;
    end,

    DisplayOverviewInfo := [ "cl", "r", function( tocs, groupname )
    local value, private, j, toc, record, i, pos, rel;

    value:= false;
    private:= false;
    for j in [ 1 .. Length( tocs ) ] do
      toc:= tocs[j];
      if IsBound( toc.( groupname ) ) then
        record:= toc.( groupname );
        if IsBound( record.classes ) and not IsEmpty( record.classes ) then
          value:= true;
        elif IsBound( record.cyc2ccl ) and IsBound( record.cyclic ) then
          for i in record.cyc2ccl do
            # Check that for scripts of the form
            # `<groupname>G<i>cycW<n>-cclsW<m>',
            # a script of the form `<groupname>G<i>-cycW<n>' is available.
            pos:= PositionSublist( i[2], "cycW" );
            rel:= Concatenation( i[2]{ [ 1 .. pos-1 ] }, "-",
                      i[2]{ [ pos .. Position( i[2], '-' ) - 1 ] } );
            if ForAny( record.cyclic, x -> x[2] = rel ) then
              value:= true;
              break;
            fi;
          od;
        fi;
        if value then
          if 1 < j then
            private:= true;
          fi;
          break;
        fi;
      fi;
    od;
    if value then
      value:= "+";
    else
      value:= "-";
    fi;
    return [ value, private ];
    end ],

    DisplaySLP := function( tocs, name, std, stdavail )
      local ccl, c2c, cyc, private, j, toc, record, i, pos, rel;

      # (The information can be stored either directly or via two scripts
      # in `cyclic' and `cyc2ccl'.)
      ccl:= [];
      c2c:= [];
      cyc:= [];
      private:= "";
      for j in [ 1 .. Length( tocs ) ] do
        toc:= tocs[j];
        if IsBound( toc.( name ) ) then
          record:= toc.( name );
          if IsBound( record.classes ) then
            for i in record.classes do
              if std = true or i[1] in std then
                if 1 < j then
                  private:= AtlasOfGroupRepresentationsInfo.markprivate;
                fi;
                Add( ccl, i );
              fi;
            od;
          fi;
          if IsBound( record.cyc2ccl ) then
            for i in record.cyc2ccl do
              if std = true or i[1] in std then
                if 1 < j then
                  private:= AtlasOfGroupRepresentationsInfo.markprivate;
                fi;
                Add( c2c, i );
              fi;
            od;
          fi;
          if IsBound( record.cyclic ) then
            for i in record.cyclic do
              if std = true or i[1] in std then
                if 1 < j then
                  private:= AtlasOfGroupRepresentationsInfo.markprivate;
                fi;
                Add( cyc, i );
              fi;
            od;
          fi;
        fi;
      od;
      for i in c2c do

        # Check if for scripts of the form `<groupname>G<i>cycW<n>-cclsW<m>',
        # a script of the form `<groupname>G<i>-cycW<n>' is available.
        pos:= PositionSublist( i[2], "cycW" );
        rel:= Concatenation( i[2]{ [ 1 .. pos-1 ] }, "-",
                  i[2]{ [ pos .. Position( i[2], '-' ) - 1 ] } );
        if ForAny( cyc, x -> x[2] = rel ) then
          Add( ccl, i );
        fi;
      od;

      if IsEmpty( ccl ) then
        return [];
      elif 1 < Length( stdavail ) then
        return [ Concatenation( "class repres. of G available",
                                " for std. generators ",
                                String( Set( List( ccl, x -> x[1] ) ) ),
                                private, "\n" ) ];
      else
        return [ Concatenation( "class repres. of G available",
                                private, "\n" ) ];
      fi;
    end,

    # entry: `[ <std>, <file> ]',
    # conditions: `[ "classes" ]'
    AccessSLP := function( record, std, conditions )
      local entry, pos, rel, entry2;
      if conditions[1] <> "classes" then
        return fail;
      fi;
      if IsBound( record.classes ) then
        for entry in record.classes do
          if std = true or entry[1] in std then
            return entry{ [ 2, 1 ] };
          fi;
        od;
      fi;
      if IsBound( record.cyclic ) and IsBound( record.cyc2ccl ) then
        for entry in record.cyc2ccl do
          if std = true or entry[1] in std then

            # Check if for scripts of the form
            # `<groupname>G<i>cycW<n>-cclsW<m>',
            # a script of the form `<groupname>G<i>-cycW<n>' is available.
            pos:= PositionSublist( entry[2], "cycW" );
            rel:= Concatenation( entry[2]{ [ 1 .. pos-1 ] }, "-",
                  entry[2]{ [ pos .. Position( entry[2], '-' ) - 1 ] } );
            for entry2 in record.cyclic do
              if entry2[2] = rel and ( std = true or entry2[1] in std ) then
                return [ [ entry[2], entry2[2] ], entry[1] ];
              fi;
            od;
          fi;
        od;
      fi;
      return fail;
    end,

    TestWords := function( tocid, name, file )
      return AGRTestWordsDefault( tocid, name, file, true );
    end
    ) );


#############################################################################
##
#D  Straight line programs for representatives of cyclic subgroups
##
AGRDeclareDataType( "slp", "cyclic", rec(

    # `<groupname>G<i>-cycW<n>'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                          [ "cycW", IsDigitChar ] ],
                        [ ParseBackwards, ParseForwards ] ],

    # `[ <i>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      Add( list, Concatenation( entry{ [ 3 ] }, [ name ] ) );
      return true;
    end,

    DisplayOverviewInfo := [ "cyc", "r", function( tocs, groupname )
    local value, private, j, toc, record, new;

    value:= false;
    private:= false;
    for j in [ 1 .. Length( tocs ) ] do
      toc:= tocs[j];
      if IsBound( toc.( groupname ) ) then
        record:= toc.( groupname );
        if IsBound( record.cyclic ) then
          new:= not IsEmpty( record.cyclic );
          if 1 < j and new then
            private:= true;
          fi;
          value:= value or new;
        fi;
      fi;
    od;
    if value then
      value:= "+";
    else
      value:= "-";
    fi;
    return [ value, private ];
    end ],

    DisplaySLP := function( tocs, name, std, stdavail )
      local cyc, private, j, toc, record, i;
      cyc:= [];
      private:= "";
      for j in [ 1 .. Length( tocs ) ] do
        toc:= tocs[j];
        if IsBound( toc.( name ) ) then
          record:= toc.( name );
          if IsBound( record.cyclic ) then
            for i in record.cyclic do
              if std = true or i[1] in std then
                if 1 < j then
                  private:= AtlasOfGroupRepresentationsInfo.markprivate;
                fi;
                Add( cyc, i[2] );
              fi;
            od;
          fi;
        fi;
      od;

      if IsEmpty( cyc ) then
        return [];
      elif 1 < Length( stdavail ) then
        return [ Concatenation( "repres. of cyclic subgroups of G available",
                                " for std. generators ",
                                String( Set( List( cyc, x -> x[1] ) ) ),
                                private, "\n" ) ];
      else
        return [ Concatenation( "repres. of cyclic subgroups of G available",
                                private, "\n" ) ];
      fi;
    end,

    # entry: `[ <std>, <file> ]',
    # conditions: `[ "cyclic" ]'
    AccessSLP := function( record, std, conditions )
      local entry;
      if conditions[1] = "cyclic" and IsBound( record.cyclic ) then
        for entry in record.cyclic do
          if std = true or entry[1] in std then
            return entry{ [ 2, 1 ] };
          fi;
        od;
      fi;
      return fail;
    end,

    TestWords := function( tocid, name, file )
      return AGRTestWordsDefault( tocid, name, file, true );
    end
    ) );


#############################################################################
##
#D  Straight line programs for computing class representatives from
#D      representatives of cyclic subgroups
##
AGRDeclareDataType( "slp", "cyc2ccl", rec(

    # `<groupname>G<i>cycW<n>-cclsW<m>'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar, "cycW", IsDigitChar ],
                          [ "cclsW", IsDigitChar ] ],
                        [ ParseBackwards, ParseForwards ] ],

    # `[ <i>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      Add( list, Concatenation( entry{ [ 3 ] }, [ name ] ) );
      return true;
    end,

    # entry: `[ <std>, <file> ]',
    # conditions: `[ "cyc2ccl" ]'
    AccessSLP := function( record, std, conditions )
      local entry;
      if conditions[1] = "cyc2ccl" and IsBound( record.cyc2ccl ) then
        for entry in record.cyc2ccl do
          if std = true or entry[1] in std then
            return entry{ [ 2, 1 ] };
          fi;
        od;
      fi;
      return fail;
    end,

    TestWords := function( tocid, name, file )
      return AGRTestWordsDefault( tocid, name, file, true );
    end
    ) );


#############################################################################
##
#D  Straight line programs for computing standard generators of
#D      maximal subgroups
##
AGRDeclareDataType( "slp", "maxstd", rec(
    # `<groupname>G<i>max<k>W<n>-<subgroupname>G<j>W<m>'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar, "max", IsDigitChar,
                            "W", IsDigitChar ],
                          [ IsChar, "G", IsDigitChar, "W", IsDigitChar ] ],
                        [ ParseBackwards, ParseBackwards ] ],

    # `[ <i>, <k>, <subgroupname>, <j>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      Add( list, Concatenation( entry{ [ 3, 5, 8, 10 ] }, [ name ] ) );
      return true;
    end,

    PostprocessFileInfo := function( toc, record )
      local list, i;
      list:= record.maxstd;
      for i in [ 1 .. Length( list ) ] do
        if not IsBound( toc.( list[i][3] ) ) then
          Info( InfoAtlasRep, 3,
                "t.o.c. construction: ignoring name `", list[i][5], "'" );
          Unbind( list[i] );
        fi;
      od;
      if not IsDenseList( list ) then
        record.maxstd:= Compacted( list );
      fi;
    end ) );


#############################################################################
##
#D  Straight line programs for computing images of standard generators
#D      under outer automorphisms
##
AGRDeclareDataType( "slp", "out", rec(

    # `<groupname>G<i>-a<outname>W<n>'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                          [  "a", IsChar, "W", IsDigitChar ] ],
                        [ ParseBackwards, ParseBackwardsWithPrefix ] ],

    # `[ <i>, <nam>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      local std, descr, pos, dashes, order, index;
      std:= entry[3];
      descr:= entry[5];
      pos:= Position( descr, 'p' );
      if pos = fail then
        dashes:= "";
        pos:= Length( descr ) + 1;
      elif pos = Length( descr ) then
        dashes:= "'";
      else
        dashes:= Int( descr{ [ pos+1 .. Length( descr ) ] } );
        if dashes = fail then
          return false;
        fi;
        dashes:= ListWithIdenticalEntries( dashes, '\'' );
      fi;
      descr:= descr{ [ 1 .. pos-1 ] };
      pos:= Position( descr, '_' );
      if pos = fail then
        order:= descr;
        index:= "";
      else
        order:= descr{ [ 1 .. pos-1 ] };
        index:= descr{ [ pos+1 .. Length( descr ) ] };
      fi;
      if Int( order ) = fail or Int( index ) = fail then
        return false;
      elif order = "" then
        order:= "2";
      fi;
      if index <> "" then
        order:= Concatenation( order, "_", index );
      fi;
      order:= Concatenation( order, dashes );
      Add( list, [ std, order, name ] );
      return true;
    end,

    DisplayOverviewInfo := [ "out", "r", function( tocs, groupname )
      local value, private, j, toc, record, new;

      value:= "";;
      private:= false;
      for j in [ 1 .. Length( tocs ) ] do
        toc:= tocs[j];
        if IsBound( toc.( groupname ) ) then
          record:= toc.( groupname );
          if IsBound( record.out ) then
            new:= Set( List( record.out, x -> x[2] ) );
            if 1 < j and not IsEmpty( new ) then
              private:= true;
            fi;
            UniteSet( value, new );
          fi;
        fi;
      od;
      if not IsEmpty( value ) then
        value:= JoinStringsWithSeparator( value, "," );
      fi;
      return [ value, private ];
    end ],

    DisplaySLP := function( tocs, name, std, stdavail )
      local out, private, j, toc, record, i;

      out:= [];
      private:= "";

      for j in [ 1 .. Length( tocs ) ] do
        toc:= tocs[j];
        if IsBound( toc.( name ) ) then
          record:= toc.( name );
          if IsBound( record.out ) then
            for i in record.out do
              if std = true or i[1] in std then
                if 1 < j then
                  private:= AtlasOfGroupRepresentationsInfo.markprivate;
                fi;
                Add( out, i[2] );
              fi;
            od;
          fi;
        fi;
      od;
      if not IsEmpty( out ) then
        out:= [ Concatenation( "available automorphisms:  ", String( out ),
                    private, "\n" ) ];
      fi;
      return out;
    end,

    # entry: `[ <std>, <autname>, <file> ]',
    # conditions: `[ "automorphism", <autname> ]'
    AccessSLP := function( record, std, conditions )
      local entry;
      if conditions[1] = "automorphism" and IsBound( record.out ) then
        for entry in record.out do
          if     ( std = true or entry[1] in std )
             and entry[2] = conditions[2] then
            return entry{ [ 3, 2 ] };
          fi;
        od;
      fi;
      return fail;
    end,

    # It would be good to check whether the order of the automorphism
    # fits to the name of the script, but the scripts do not describe
    # automorphisms of minimal possible order.
    # (So the power given by the name of the script is an inner
    # automorphism; how could we check this with reasonable effort?)
    # Thus we check just whether the name fits to the structure of the
    # outer automorphism group and to the order of the automorphism.
    # (We copy the relevant part of the code of `AGRTestWordsDefault'
    # into this function.)
    TestWords := function( tocid, name, file )
      local filename, prog, prg, gens, gapname, pos, claimedorder, tbl,
            outinfo, imgs, order;

      # Read the program.
      if tocid = "local" then
        tocid:= "dataword";
      fi;
      filename:= FilenameAtlas( tocid, name, file );
      if filename = fail then
        Print( "#E  no filename `", file, "' for `", name, "'\n" );
        return false;
      fi;
      prog:= ScanStraightLineProgram( filename );

      # Check consistency.
      if prog = fail or not IsInternallyConsistent( prog.program ) then
        Print( "#E  program `", file, "' not internally consistent\n" );
        return false;
      fi;
      prg:= prog.program;

      # Create the list of (trivial) generators.
      gens:= ListWithIdenticalEntries( NrInputsOfStraightLineProgram( prg ),
                                       () );

      # Run the program.
      gens:= ResultOfStraightLineProgram( prg, gens );

      # Get the {\GAP} name of `name'.
      gapname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                       pair -> name = pair[2] );
      if gapname = fail then
        Print( "#E  problem: no GAP name for `", name, "'\n" );
        return false;
      fi;
      gapname:= gapname[1];

      # Get the order of the automorphism from the filename.
      pos:= PositionSublist( file, "-a" );
      claimedorder:= file{ [ pos+2 .. Length( file ) ] };
      pos:= Position( claimedorder, 'W' );
      claimedorder:= claimedorder{ [ 1 .. pos-1 ] };
      pos:= Position( claimedorder, 'p' );
      if pos <> fail then
	if not ForAll( claimedorder{ [ pos+1 .. Length( claimedorder ) ] },
	               IsDigitChar ) then
	  Print( "#E  wrong number of dashes in `", file, "'\n" );
	  return false;
	elif claimedorder{ [ pos+1 .. Length( claimedorder ) ] } = "0" then
          Print( "#E  wrong name `", file, "'\n" );
	  return false;
	fi;
        claimedorder:= claimedorder{ [ 1 .. pos-1 ] };
      fi;
      pos:= Position( claimedorder, '_' );
      if pos <> fail then
        claimedorder:= claimedorder{ [ 1 .. pos-1 ] };
      fi;
      if not ForAll( claimedorder, IsDigitChar ) then
        Print( "#E  wrong name `", file, "'\n" );
	return false;
      fi;
      claimedorder:= Int( claimedorder );

      # Get the structure of the automorphism group.
      # If this group is cyclic then we compare orders.
      tbl:= CharacterTable( gapname );
      if tbl <> fail and IsBound( tbl!.extInfo ) then
        outinfo:= tbl!.extInfo[2];
        if    outinfo = "" then
          Print( "#E  automorphism `", file,
                 "' for group without outer automorphisms\n" );
          return false;
        elif outinfo <> "2" and claimedorder = 0 then
          Print( "#E  automorphism `", file,
                 "' but the outer automorphism is not unique\n" );
          return false;
        elif Int( outinfo ) <> fail and claimedorder <> 0
             and Int( outinfo ) mod claimedorder <> 0 then
          Print( "#E  automorphism `", file,
                 "' for outer automorphism group ", outinfo, "\n" );
          return false;
        fi;
      fi;

      if claimedorder = 0 then
        claimedorder:= 2;
      fi;

      # Get generators of the group in question.
      gens:= OneAtlasGeneratingSet( gapname );
      if gens <> fail then
        gens:= gens.generators;

        # Compute the order of the automorphism.
        imgs:= ResultOfStraightLineProgram( prg, gens );
        order:= 1;
        while imgs <> gens do
          imgs:= ResultOfStraightLineProgram( prg, imgs );
          order:= order + 1;
        od;

        if order mod claimedorder <> 0 then
          Print( "#E  order ", order, " of automorphism `", file,
                 "' not divisible by ", claimedorder, "\n" );
          return false;
        fi;
      fi;

      return true;
    end

    ) );


#############################################################################
##
#D  Straight line programs for switching between different standardizations
##
AGRDeclareDataType( "slp", "switch", rec(
    # `<groupname>G<i>-G<j>W<n>'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                          [  "G", IsDigitChar, "W", IsDigitChar ] ],
                        [ ParseBackwards, ParseForwards ] ],

    # `[ <i>, <j>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      Add( list, [ entry[3], entry[5], name ] );
      return true;
    end,

    DisplaySLP := function( tocs, name, std, stdavail )
      local switch, private, j, toc, record, i;

      switch:= [];
      private:= "";

      for j in [ 1 .. Length( tocs ) ] do
        toc:= tocs[j];
        if IsBound( toc.( name ) ) then
          record:= toc.( name );
          if IsBound( record.switch ) then
            for i in record.switch do
              if std = true or i[1] in std then
                if 1 < j then
                  private:= AtlasOfGroupRepresentationsInfo.markprivate;
                fi;
                Add( switch, Concatenation( String( i[1] ), " -> ",
                                 String( i[2] ) ) );
              fi;
            od;
          fi;
        fi;
      od;
      if not IsEmpty( switch ) then
        switch:= [ Concatenation( "available restandardizations:  ",
                       String( switch ), private, "\n" ) ];
      fi;
      return switch;
    end,

    # entry: `[ <std>, <descr>, <file> ]',
    # conditions: `[ "restandardize", <std2> ]'
    AccessSLP := function( record, std, conditions )
      local entry;
      if conditions[1] = "restandardize" and IsBound( record.switch ) then
        for entry in record.switch do
          if     ( std = true or entry[1] in std )
             and conditions[2] = entry[2] then
            return entry{ [ 3, 1, 2 ] };
          fi;
        od;
      fi;
      return fail;
    end
    ) );


#############################################################################
##
#D  Other straight line programs
##
AGRDeclareDataType( "slp", "otherscripts", rec(

    # `<groupname>G<i>-X<descr>W<n>'
    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
                          [ "X", IsChar, "W", IsDigitChar ] ],
                        [ ParseBackwards, ParseBackwardsWithPrefix ] ],

    # `[ <i>, <descr>, <filename> ]'
    AddFileInfo := function( list, entry, name )
      Add( list, Concatenation( entry{ [ 3, 5 ] }, [ name ] ) );
      return true;
    end,

    DisplaySLP := function( tocs, name, std, stdavail )
      local result, other, private, j, toc, record, i;
      other:= [];
      private:= "";
      for j in [ 1 .. Length( tocs ) ] do
        toc:= tocs[j];
        if IsBound( toc.( name ) ) then
          record:= toc.( name );
          if IsBound( record.otherscripts ) then
            for i in record.otherscripts do
              if std = true or i[1] in std then
                if 1 < j then
                  private:= AtlasOfGroupRepresentationsInfo.markprivate;
                fi;
                Add( other, i[2] );
              fi;
            od;
          fi;
        fi;
      od;
      if not IsEmpty( other ) then
        other:= [ Concatenation( "available other scripts:  ",
                      String( other ), private, "\n" ) ];
      fi;
      return other;
    end,

    # entry: `[ <std>, <descr>, <file> ]',
    # conditions: `[ "other", <descr> ]'
    AccessSLP := function( record, std, conditions )
      local entry;
      if conditions[1] = "other" and IsBound( record.otherscripts ) then
        for entry in record.otherscripts do
          if     ( std = true or entry[1] in std )
             and entry[2] = conditions[2] then
            return entry{ [ 3, 1 ] };
          fi;
        od;
      fi;
      return fail;
    end
    ) );


#############################################################################
##
##  Read the server's table of contents.
##  Preferably a user file is taken because it might be a more recent version,
##  otherwise the file that was distributed with the package is read.
##
##  Note that `types.g' is notified with `DeclareAutoreadableVariables',
##  in order to delay the evaluation of the data.
##
if IsBound( ATLASREP_TOCFILE ) then
  if not IsReadableFile( ATLASREP_TOCFILE ) then
    Error( "the file for the global `ATLASREP_TOCFILE' is not readable" );
  elif not READ( ATLASREP_TOCFILE ) then
    Error( "problem reading the file for the global `ATLASREP_TOCFILE'" );
  fi;
else
  ReadPkg( "atlasrep", "gap/atlasprm.g" );
fi;


#############################################################################
##
#E

