#############################################################################
##
#W  interfac.gi          GAP 4 package `atlasrep'               Thomas Breuer
##
#H  @(#)$Id: interfac.gi,v 1.42 2004/03/19 16:36:43 gap Exp $
##
#Y  Copyright (C)  2001,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
##
##  This file contains the implementation part of the ``high level'' {\GAP}
##  interface to the {\ATLAS} of Group Representations.
##
Revision.( "atlasrep/gap/interfac_gi" ) :=
    "@(#)$Id: interfac.gi,v 1.42 2004/03/19 16:36:43 gap Exp $";


#############################################################################
##
#F  DisplayAtlasInfoOverview( <gapnames>, <tocs> )
##
BindGlobal( "DisplayAtlasInfoOverview", function( gapnames, tocs )
    local columns,
          widths,
          width,
          i, j;

    # Consider only those names for which actually information is available.
    # (The ordering shall be the same as in the input.)
    if gapnames = "all" then
      gapnames:= AtlasOfGroupRepresentationsInfo.GAPnames;
    else
      gapnames:= Filtered( List( gapnames,
                     i -> First( AtlasOfGroupRepresentationsInfo.GAPnames,
                                 pair -> pair[1] = i ) ),
                           x -> x <> fail );
    fi;
    gapnames:= Filtered( gapnames,
                   x -> ForAny( tocs, toc -> IsBound( toc.( x[2] ) ) ) );
    if IsEmpty( gapnames ) then
      return;
    fi;

    # Compute the data of the columns.
    columns:= [ [ "group", "l", List( gapnames, x -> [ x[1], false ] ) ] ];
    Append( columns, List( AGRDataTypes( "rep", "slp" ),
                    type ->
           [ type[2].DisplayOverviewInfo[1],
             type[2].DisplayOverviewInfo[2],
             List( gapnames,
                   n -> type[2].DisplayOverviewInfo[3]( tocs, n[2] ) ) ] ) );

    # Compute the appropriate column widths.
    widths:= [];
    for i in columns do
      width:= Maximum( Length( i[1] ),       # the header string shall fit
                  Maximum( List( i[3], y -> Length( y[1] ) ) ) );
      if width <> 0 then
        width:= width + 2;
      fi;
      if i[2] = "l" then
        width:= -width;
      fi;
      Add( widths, width );
    od;

    # Print the header line.
    for j in [ 1 .. Length( columns ) ] do
      Print( FormattedString( columns[j][1], widths[j] ) );
    od;
    Print( "\n",
           ListWithIdenticalEntries( Sum( widths, AbsInt ), '-' ),
           "\n" );

    # Print the information for each group.
    for i in [ 1 .. Length( gapnames ) ] do
      if ForAny( columns, x -> x[3][i][2] ) then
        columns[1][3][i][1]:= Concatenation( columns[1][3][i][1],
            AtlasOfGroupRepresentationsInfo.markprivate );
      fi;
      for j in [ 1 .. Length( columns ) ] do
        Print( FormattedString( columns[j][3][i][1], widths[j] ) );
      od;
      Print( "\n" );
    od;
    end );


#############################################################################
##
#F  AtlasOfGroupRepresentationsInfoSLP( <gapname>, <tocs>, <name>, <std> )
##
BindGlobal( "AtlasOfGroupRepresentationsInfoSLP",
    function( gapname, tocs, name, std )
    local stdavail,
          j,
          toc,
          record,
          type,
          list,
          header;

    # If the standardization is prescribed then do not mention it.
    # Otherwise if all information refers to the same standardization then
    # print just one line.
    # Otherwise print the standardization for each entry.
    stdavail:= [];
    if std = true or 1 < Length( std ) then
      for toc in tocs do
        if IsBound( toc.( name ) ) then
          record:= toc.( name );
          for type in AGRDataTypes( "slp" ) do
            if IsBound( record.( type[1] ) ) then
              for list in record.( type[1] ) do
                if std = true or list[1] in std then
                  AddSet( stdavail, list[1] );
                fi;
              od;
            fi;
          od;
        fi;
      od;
    fi;

    # Create the header line.
    header:= [ "Straight line programs for G = ", gapname, ":" ];
    if Length( stdavail ) = 1 then
      Append( header, [ "    (all refer to std. generators ",
                        String( stdavail[1] ), ")" ] );
    fi;

    # Collect the info lines for the scripts.
    list:= [];
    for type in AGRDataTypes( "slp" ) do
      Append( list, type[2].DisplaySLP( tocs, name, std, stdavail ) );
    od;

    return rec( header := header,
                list   := list );
    end );


#############################################################################
##
#F  AtlasOfGroupRepresentationsInfoGroup( <gapname>, <tocs>, <name>, <std>,
#F      <conditions> )
##
BindGlobal( "AtlasOfGroupRepresentationsInfoGroup",
    function( gapname, tocs, name, std, conditions )
    local stdavail,
          nam,
          toc,
          record,
          list,
          header,
          i, j,
          nr,
          type,
          entry;

    # If the standardization is prescribed then do not mention it.
    # Otherwise if all information refers to the same standardization then
    # print just one line.
    # Otherwise print the standardization for each entry.
    stdavail:= [];
    if std = true or 1 < Length( std ) then
      for nam in AGRDataTypes( "rep" ) do
        for toc in tocs do
          if IsBound( toc.( name ) ) then
            record:= toc.( name );
            if IsBound( record.( nam[1] ) ) then
              for list in record.( nam[1] ) do
                if std = true or list[1] in std then
                  AddSet( stdavail, list[1] );
                fi;
              od;
            fi;
          fi;
        od;
      od;
    fi;

    # Construct the header line.
    header:= [ "Representations for G = ", gapname, ":" ];
    if Length( stdavail ) = 1 then
      Add( header, Concatenation(
           "    (all refer to std. generators ", String( stdavail[1] ),
           ")" ) );
    fi;

    # Collect the information for the given group.
#T sort across the tocs!
    list:= [];
    nr:= 0;
    for type in AGRDataTypes( "rep" ) do
      for j in [ 1 .. Length( tocs ) ] do
        toc:= tocs[j];
        if IsBound( toc.( name ) ) then
          record:= toc.( name );
          if IsBound( record.( type[1] ) ) then
            for i in record.( type[1] ) do
              nr:= nr + 1;
              if std = true or i[1] in std then
                if type[2].AccessGroupCondition( i,
                       ShallowCopy( conditions ) ) then
                  entry:= type[2].DisplayGroup( i );
                  if 1 < j then
                    Append( entry,
                        AtlasOfGroupRepresentationsInfo.markprivate );
                  fi;
                  if 1 < Length( stdavail ) then
                    Append( entry, Concatenation(
                        ",   w.r.t. std. generators ", String( i[1] ) ) );
                  fi;
                  Add( list, [ String( nr ), entry ] );
                fi;
              fi;
            od;
          fi;
        fi;
      od;
    od;

    return rec( header := header,
                list   := list );
    end );


#############################################################################
##
#F  DisplayAtlasInfo(  )
#F  DisplayAtlasInfo( <listofnames> )
#F  DisplayAtlasInfo( <gapname>[, <std>] )
#F  DisplayAtlasInfo( <gapname>[, <std>], IsPermGroup[, true] )
#F  DisplayAtlasInfo( <gapname>[, <std>], NrMovedPoints, <n> )
#F  DisplayAtlasInfo( <gapname>[, <std>], IsMatrixGroup[, true] )
#F  DisplayAtlasInfo( <gapname>[, <std>][, Characteristic, <p>]
#F                                      [, Dimension, <n>] )
#F  DisplayAtlasInfo( <gapname>[, <std>], IsStraightLineProgram )
##
InstallGlobalFunction( DisplayAtlasInfo, function( arg )
    local tocs,
          gapname,
          groupname,
          std,
          argpos,
          info1,
          len,
          line,
          info2;

    if AtlasOfGroupRepresentationsInfo.remote = true then
      tocs:= [ AtlasTableOfContents( "remote" ).TableOfContents ];
    else
      tocs:= [ AtlasTableOfContents( "local" ).TableOfContents ];
    fi;
    Append( tocs, List( AtlasOfGroupRepresentationsInfo.private,
        x -> AtlasOfGroupRepresentationsInfo.TableOfContents.( x[2] ) ) );

    if   Length( arg ) = 0 then
      DisplayAtlasInfoOverview( "all", tocs );
      return;
    elif Length( arg ) = 1 and IsList( arg[1] )
                           and ForAll( arg[1], IsString ) then
      DisplayAtlasInfoOverview( arg[1], tocs );
      return;
    elif not IsString( arg[1] ) then
      Error( "usage: DisplayAtlasInfo( [<listofnames>] ),\n",
          "DisplayAtlasInfo( <gapname>[,<std>][,IsPermGroup] ),\n",
          "DisplayAtlasInfo( <gapname>[,<std>][,NrMovedPoints,<n>] ),\n",
          "DisplayAtlasInfo( <gapname>[,<std>][,IsMatrixGroup] ),\n",
          "DisplayAtlasInfo( <gapname>[,<std>][,Characteristic, <p>]\n",
          "                                   [,Dimension, <n>] ),\n",
          "DisplayAtlasInfo( <gapname>[,<std>][,IsStraightLineProgram] ),\n",
          "DisplayAtlasInfo( <gapname>[,<std>],<conditions> )\n" );
    fi;

    gapname:= arg[1];
    groupname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                       pair -> pair[1] = gapname );
    if groupname = fail then
      Info( InfoAtlasRep, 1,
            "DisplayAtlasInfo: no group with GAP name `", gapname, "'" );
      return;
    fi;

    if Length( arg ) = 1 or not ( IsInt( arg[2] ) or IsList( arg[2] ) ) then
      std:= true;
      argpos:= 2;
    else
      std:= arg[2];
      if IsInt( std ) then
        std:= [ std ];
      fi;
      argpos:= 3;
    fi;

    if Length( arg ) <> argpos or arg[ argpos ] <> IsStraightLineProgram then

      # `DisplayAtlasInfo( <gapname>[, <std>][, <conditions>] )'
      info1:= AtlasOfGroupRepresentationsInfoGroup( gapname, tocs,
                  groupname[2], std, arg{ [ argpos .. Length( arg ) ] } );
      if not IsEmpty( info1.list ) then
        Print( Concatenation( info1.header ),
               "\n",
               ListWithIdenticalEntries( Length( gapname ) + 25, '-' ),
               "\n" );
        len:= Maximum( List( info1.list, x -> Length( x[1] ) ) );
        for line in info1.list do
          Print( FormattedString( line[1], len ), ": ", line[2], "\n" );
        od;
      fi;
    fi;
    if ( Length( arg ) = argpos and arg[ argpos ] = IsStraightLineProgram )
       or Length( arg ) < argpos then

      # `DisplayAtlasInfo( <gapname>[, <std>][, IsStraightLineProgram] )'
      info2:= AtlasOfGroupRepresentationsInfoSLP( gapname, tocs,
                  groupname[2], std );
      if not IsEmpty( info2.list ) then
        if IsBound( info1 ) and not IsEmpty( info1.list ) then
          Print( "\n" );
        fi;
        Print( Concatenation( info2.header ),
               "\n",
               ListWithIdenticalEntries( Length( gapname ) + 32, '-' ),
               "\n",
               Concatenation( info2.list ) );
      fi;

    fi;
    end );


#############################################################################
##
#F  AtlasGenerators( <gapname>, <repnr> )
#F  AtlasGenerators( <gapname>, <repnr>, <maxnr> )
#F  AtlasGenerators( <identifier> )
##
##  <identifier> is a list containing at the first position the string
##  <gapname>,
##  at the second position a string or a list of strings
##  (describing filenames),
##  at the third position a positive integer denoting the standardization of
##  the representation,
##  at the fourth position a positive integer describing the common ring of
##  the generators,
##  and at the fifth position, if bound, a positive integer denoting the
##  number of the maximal subgroup to which the representation is restricted.
##
InstallGlobalFunction( AtlasGenerators, function( arg )
    local identifier,
          gapname,
          groupname,
          file,
          prefix,
          gens,
          name,
          filename,
          prog,
          tocs,
          try,
          res,
          type,
          id;

    if Length( arg ) = 1 then

      # `AtlasGenerators( <identifier> )'
      identifier:= arg[1];
      gapname:= identifier[1];
      if Length( gapname ) = 2 and IsString( gapname[1] ) then
        prefix:= gapname[1];
        gapname:= gapname[2];
      else
        prefix:= "datagens";
      fi;
      groupname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                         pair -> pair[1] = gapname );
      file:= identifier[2];

      if IsString( file ) then

        # one file containing all generators
        filename:= FilenameAtlas( prefix, groupname[2], file );
        if filename = fail then
          return fail;
        fi;
        gens:= AtlasDataCharacteristicZeroFile( filename ).generators;

      elif IsList( file ) and ForAll( file, IsString ) then

        # several files containing one generator each
        gens:= [];
        for name in file do
          filename:= FilenameAtlas( prefix, groupname[2], name );
          if filename = fail then
            return fail;
          fi;
          Add( gens, ScanMeatAxeFile( filename, identifier[4] ) );
        od;
        if ForAll( gens, x -> IsList( x ) and IsPerm( x[1] ) ) then
          gens:= Concatenation( gens );
        fi;

      else
        Error( "<file> must be a string or a list of strings" );
      fi;

      if IsBound( identifier[5] ) then

        # Compute the straight line program for the restriction
        # (w.r.t. the correct standardization).
        prog:= AtlasStraightLineProgram( gapname, identifier[3],
                                         identifier[5] );
        if prog = fail then
          return fail;
        fi;

        # Evaluate the straight line program.
        gens:= ResultOfStraightLineProgram( prog.program, gens );

      fi;

      # Return the result.
      return Immutable( rec( generators      := gens,
                             standardization := identifier[3],
                             identifier      := Immutable( identifier ) ) );

    elif  ( Length( arg ) = 2 and IsString( arg[1] ) and IsPosInt( arg[2] ) )
       or ( Length( arg ) = 3 and IsString( arg[1] ) and IsPosInt( arg[2] )
                              and IsPosInt( arg[3] ) ) then

      # `AtlasGenerators( <gapname>, <repnr>[, <maxnr>] )'
      if AtlasOfGroupRepresentationsInfo.remote = true then
        tocs:= [ AtlasTableOfContents( "remote" ).TableOfContents ];
      else
        tocs:= [ AtlasTableOfContents( "local" ).TableOfContents ];
      fi;
      Append( tocs, List( AtlasOfGroupRepresentationsInfo.private,
          x -> AtlasOfGroupRepresentationsInfo.TableOfContents.( x[2] ) ) );

      gapname:= arg[1];
      groupname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                         pair -> pair[1] = gapname );

      if groupname = fail then
        Info( InfoAtlasRep, 1,
              "AtlasGenerators: no group with GAP name `", gapname, "'" );
        return fail;
      fi;

      try:= function( repnr, type )
        local j, toc, record;
        for j in [ 1 .. Length( tocs ) ] do
          toc:= tocs[j];
          if IsBound( toc.( groupname[2] ) ) then
            record:= toc.( groupname[2] );
            if IsBound( record.( type ) ) then
              if repnr <= Length( record.( type ) ) then
                return [ j, record.( type )[ repnr ] ];
              fi;
              repnr:= repnr - Length( record.( type ) );
            fi;
          fi;
        od;
        return repnr;
      end;

      res:= arg[2];
      for type in AGRDataTypes( "rep" ) do
        res:= try( res, type[1] );
        if not IsInt( res ) then
          break;
        fi;
      od;
      if IsInt( res ) then
        return fail;
      fi;

      if res[1] = 1 then
        res:= res[2];
        identifier:= [ gapname, res[ Length(res) ], res[1], res[2] ];
      else
        id:= AtlasOfGroupRepresentationsInfo.private[ res[1]-1 ][2];
        res:= res[2];
        identifier:= [ [ id, gapname ], res[ Length(res) ], res[1], res[2] ];
      fi;
      if IsBound( arg[3] ) then
        identifier[5]:= arg[3];
      fi;

      # Delegate to the one argument version.
      return AtlasGenerators( identifier );

    else
      Error( "usage: AtlasGenerators( <gapname>,<repnr>[,<maxnr>] ) or\n",
             "       AtlasGenerators( <identifier> )" );
    fi;
    end );


#############################################################################
##
#F  AtlasGeneratingSetInfo( <conditions>, "one" )
#F  AtlasGeneratingSetInfo( <conditions>, "all" )
##
##  This function does the work for `OneAtlasGeneratingSetInfo' and
##  `AllAtlasGeneratingSetInfos'.
##
BindGlobal( "AtlasGeneratingSetInfo", function( conditions, mode )
    local gapnames,
          std,
          tocs,
          result,
          gapname,
          groupname,
          types,
          type,
          j,
          toc,
          record,
          i,
          id;

    if Length( conditions ) = 0 or not IsString( conditions[1] ) then
      gapnames:= List( AtlasOfGroupRepresentationsInfo.GAPnames,
                       pair -> pair[1] );
    else
      gapnames:= [ conditions[1] ];
      conditions:= conditions{ [ 2 .. Length( conditions ) ] };
    fi;
    if 1 <= Length( conditions ) and
       ( IsPosInt( conditions[1] ) or IsList( conditions[1] ) ) then
      std:= conditions[1];
      if IsPosInt( std ) then
        std:= [ std ];
      fi;
      conditions:= conditions{ [ 2 .. Length( conditions ) ] };
    else
      std:= true;
    fi;

    if AtlasOfGroupRepresentationsInfo.remote = true then
      tocs:= [ AtlasTableOfContents( "remote" ).TableOfContents ];
    else
      tocs:= [ AtlasTableOfContents( "local" ).TableOfContents ];
    fi;
    Append( tocs, List( AtlasOfGroupRepresentationsInfo.private,
        x -> AtlasOfGroupRepresentationsInfo.TableOfContents.( x[2] ) ) );

    result:= [];

    for gapname in gapnames do
      groupname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                         pair -> pair[1] = gapname );
      if groupname <> fail then

        # Loop over the relevant representations.
        for type in AGRDataTypes( "rep" ) do
          for j in [ 1 .. Length( tocs ) ] do
            toc:= tocs[j];
            if IsBound( toc.( groupname[2] ) ) then
              record:= toc.( groupname[2] );
              if IsBound( record.( type[1] ) ) then
                for i in record.( type[1] ) do
                  if     ( std = true or i[1] in std )
                     and type[2].AccessGroupCondition( i,
                             ShallowCopy( conditions ) ) then
                    if j = 1 then
                      id:= [ gapname, i[ Length(i) ], i[1], i[2] ];
                    else
                      id:= AtlasOfGroupRepresentationsInfo.private[ j-1 ][2];
                      id:= [ [ id, gapname ], i[ Length(i) ], i[1], i[2] ];
                    fi;
                    if mode = "one" then
                      return rec( identifier:= id );
                    else
                      Add( result, rec( identifier:= id ) );
                    fi;
                  fi;
                od;
              fi;
            fi;
          od;
        od;

      fi;
    od;

    # We have checked all available representations.
    if mode = "one" then
      return fail;
    else
      return result;
    fi;
    end );


#############################################################################
##
#F  OneAtlasGeneratingSetInfo( [<gapname>][, <std>] )
#F  OneAtlasGeneratingSetInfo( [<gapname>][, <std>], IsPermGroup[, true] )
#F  OneAtlasGeneratingSetInfo( [<gapname>][, <std>], NrMovedPoints, <n> )
#F  OneAtlasGeneratingSetInfo( [<gapname>][, <std>], IsMatrixGroup[, true] )
#F  OneAtlasGeneratingSetInfo( [<gapname>][, <std>][, Characteristic, <p>]
#F                                                 [, Dimension, <m>] )
#F  OneAtlasGeneratingSetInfo( [<gapname>][, <std>][, Ring, <R>]
#F                                                 [, Dimension, <m>] )
##
InstallGlobalFunction( OneAtlasGeneratingSetInfo, function( arg )
    return AtlasGeneratingSetInfo( arg, "one" );
    end );


#############################################################################
##
#F  AllAtlasGeneratingSetInfos( [<gapname>][, <std>] )
#F  AllAtlasGeneratingSetInfos( [<gapname>][, <std>], IsPermGroup[, true] )
#F  AllAtlasGeneratingSetInfos( [<gapname>][, <std>], NrMovedPoints, <n> )
#F  AllAtlasGeneratingSetInfos( [<gapname>][, <std>], IsMatrixGroup[, true] )
#F  AllAtlasGeneratingSetInfos( [<gapname>][, <std>][, Characteristic, <p>]
#F                                                  [, Dimension, <m>] )
#F  AllAtlasGeneratingSetInfos( [<gapname>][, <std>][, Ring, <R>]
#F                                                  [, Dimension, <m>] )
##
InstallGlobalFunction( AllAtlasGeneratingSetInfos, function( arg )
    return AtlasGeneratingSetInfo( arg, "all" );
    end );


#############################################################################
##
#F  AtlasStraightLineProgram( <gapname>[, <std>][, "maxes"], <maxnr> )
#F  AtlasStraightLineProgram( <gapname>[, <std>], "classes" )
#F  AtlasStraightLineProgram( <gapname>[, <std>], "cyclic" )
#F  AtlasStraightLineProgram( <gapname>[, <std>], "automorphism", <autname> )
#F  AtlasStraightLineProgram( <gapname>, <std>, "restandardize", <std2> )
#F  AtlasStraightLineProgram( <gapname>[, <std>], "other", <descr> )
#F  AtlasStraightLineProgram( <identifier> )
##
##  <identifier> is a list containing at the first position the string
##  <gapname>,
##  at the second position a string or a list of strings
##  (describing the filenames involved),
##  and at the third position a positive integer denoting the standardization
##  of the program.
##
InstallGlobalFunction( AtlasStraightLineProgram, function( arg )
    local identifier,
          gapname,
          prefix,
          groupname,
          file,
          progs,
          name,
          filename,
          prog,
          i,
          outputs,
          tocs,
          toc,
          std,
          maxnr,
          record,
          maxes,
          pair,
          pos,
          n,
          result,
          j,
          id,
          argpos, conditions, type;

    if Length( arg ) = 1 then

      # `AtlasStraightLineProgram( <identifier> )'
      identifier:= arg[1];
      gapname:= identifier[1];
      if Length( gapname ) = 2 and IsString( gapname[1] ) then
        prefix:= gapname[1];
        gapname:= gapname[2];
      else
        prefix:= "dataword";
      fi;
      groupname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                         pair -> pair[1] = gapname );
      file:= identifier[2];

      if IsList( file ) and ForAll( file, IsString ) then

        # several programs to be composed
        progs:= [];
        for name in file do
          filename:= FilenameAtlas( prefix, groupname[2], name );
          if filename = fail then
            return fail;
          fi;
          Add( progs, ScanStraightLineProgram( filename ) );
        od;
        if fail in progs then
          return fail;
        fi;
        prog:= progs[1].program;
        for i in [ 2 .. Length( progs ) ] do
          prog:= CompositionOfStraightLinePrograms( prog, progs[i].program );
          if prog = fail then
            return fail;
          fi;
        od;
        if IsBound( progs[1].outputs ) then
          # Take the outputs of the last program in the composition.
          outputs:= progs[1].outputs;
        fi;

      elif IsString( file ) then

        # one file containing the program
        filename:= FilenameAtlas( prefix, groupname[2], file );
        if filename = fail then
          return fail;
        fi;
        prog:= ScanStraightLineProgram( filename );
        if prog = fail then
          return fail;
        elif IsBound( prog.outputs ) then
          outputs:= prog.outputs;
        fi;
        prog:= prog.program;

      else
        Error( "<file> must be a string or a list of strings" );
      fi;

      # Return the result.
      result:= rec( program         := prog,
                    standardization := identifier[3],
                    identifier      := identifier );
      if IsBound( outputs ) then
        result.outputs:= outputs;
      fi;
      return Immutable( result );

    fi;

    # Now handle the cases of more than one argument.
    if Length( arg ) = 0 or not IsString( arg[1] ) then
      Error( "the first argument must be the GAP name of a group" );
    fi;
    gapname:= arg[1];
    groupname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
                       pair -> pair[1] = gapname );
    if groupname = fail then
      Info( InfoAtlasRep, 1,
            "AtlasStraightLineProgram: no group with GAP name `",
            gapname, "'" );
      return fail;
    fi;

    if IsInt( arg[2] ) and 2 < Length( arg ) then
      std:= [ arg[2] ];
      argpos:= 3;
    else
      std:= true;
      argpos:= 2;
    fi;
    conditions:= arg{ [ argpos .. Length( arg ) ] };

    # `AtlasStraightLineProgram( <gapname>[, <std>][, "maxes"], <maxnr> )'
    if Length( conditions ) = 1 and IsInt( conditions[1] ) then
      conditions:= [ "maxes", conditions[1] ];
    fi;

    if AtlasOfGroupRepresentationsInfo.remote = true then
      tocs:= [ AtlasTableOfContents( "remote" ).TableOfContents ];
    else
      tocs:= [ AtlasTableOfContents( "local" ).TableOfContents ];
    fi;
    Append( tocs, List( AtlasOfGroupRepresentationsInfo.private,
        x -> AtlasOfGroupRepresentationsInfo.TableOfContents.( x[2] ) ) );

    for type in AGRDataTypes( "slp" ) do
      for j in [ 1 .. Length( tocs ) ] do
        toc:= tocs[j];
        if IsBound( toc.( groupname[2] ) ) then
          record:= toc.( groupname[2] );
          id:= type[2].AccessSLP( record, std, conditions );
          if id <> fail then
            if j = 1 then
              id:= Concatenation( [ groupname[1] ], id );
            else
              id:= Concatenation( [
                [ AtlasOfGroupRepresentationsInfo.private[ j-1 ][2],
                  groupname[1] ] ], id );
            fi;
            return AtlasStraightLineProgram( id );
          fi;
        fi;
      od;
    od;

    # No program was found.
    Info( InfoAtlasRep, 2,
          "no straight line program for conditions ", conditions );
    Info( InfoAtlasRep, 2,
          "of the group with GAP name `", groupname[1], "'" );
    return fail;
end );


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

