1#############################################################################
2##
3#W  types.g              GAP 4 package AtlasRep                 Thomas Breuer
4##
5#Y  Copyright (C)  2001,   Lehrstuhl D für Mathematik,  RWTH Aachen,  Germany
6##
7##  This file contains implementations of the actual data types used in the
8##  &ATLAS; of Group Representations.
9##
10
11
12#############################################################################
13##
14#V  AtlasOfGroupRepresentationsInfo
15##
16BindGlobal( "AtlasOfGroupRepresentationsInfo", rec(
17
18    # user parameters
19    accessFunctions := AtlasOfGroupRepresentationsAccessFunctionsDefault,
20
21    # system parameters (filled automatically)
22    GAPnames := [],
23
24    ringinfo := [],
25
26    permrepinfo := rec(),
27
28    characterinfo := rec(),
29
30    notified := [],
31
32    filenames := [],
33    newfilenames := [],
34
35    TableOfContents := rec( core   := rec(),
36                            types  := rec( rep   := [],
37                                           prg   := [],
38                                           cache := [] ),
39                            merged := rec() ),
40
41    TOC_Cache := rec(),
42    ) );
43
44
45#############################################################################
46##
47#D  Permutation representations
48##
49##  <#GAPDoc Label="type:perm:format">
50##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-p</C><M>n</M><M>id</M><C>B</C><M>m</M><C>.m</C><M>nr</M></Mark>
51##  <Item>
52##    a file in &MeatAxe; text file format
53##    containing the <M>nr</M>-th generator of a permutation representation
54##    on <M>n</M> points.
55##    An example is <C>M11G1-p11B0.m1</C>.
56##  </Item>
57##  <#/GAPDoc>
58##
59AGR.DeclareDataType( "rep", "perm", rec(
60
61    # `<groupname>G<i>-p<n><id>B<m>.m<nr>'
62    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
63                          [ "p", IsDigitChar, AGR.IsLowerAlphaOrDigitChar,
64                            "B", IsDigitChar, ".m", IsDigitChar ] ],
65                        [ ParseBackwards, ParseForwards ] ],
66
67    AddDescribingComponents := function( record, type )
68      local repid, parsed, comp, info, pos;
69
70      repid:= record.identifier[2][1];
71      if not IsString( repid ) then
72        repid:= repid[2];
73      fi;
74      parsed:= AGR.ParseFilenameFormat( repid, type[2].FilenameFormat );
75      record.p:= Int( parsed[5] );
76      record.id:= parsed[6];
77      repid:= repid{ [ 1 .. Position( repid, '.' ) - 1 ] };
78      if IsBound( AtlasOfGroupRepresentationsInfo.characterinfo.(
79                      record.groupname ) ) then
80        info:= AtlasOfGroupRepresentationsInfo.characterinfo.(
81                   record.groupname );
82        if IsBound( info[1] ) then
83          info:= info[1];
84          pos:= Position( info[2], repid );
85          if pos <> fail then
86            record.constituents:= info[1][ pos ];
87            if info[3][ pos ] <> fail then
88              record.charactername:= info[3][ pos ];
89            fi;
90          fi;
91
92        fi;
93      fi;
94      if IsBound( AtlasOfGroupRepresentationsInfo.permrepinfo.( repid ) ) then
95        repid:= AtlasOfGroupRepresentationsInfo.permrepinfo.( repid );
96        for comp in [ "isPrimitive", "orbits", "rankAction", "stabilizer",
97                      "transitivity", "maxnr" ] do
98          if IsBound( repid.( comp ) ) and repid.( comp ) <> "???" then
99            record.( comp ):= repid.( comp );
100          fi;
101        od;
102      fi;
103    end,
104
105    # `[ <i>, <n>, <id>, <m>, <filenames> ]'
106    AddFileInfo := function( list, entry, name )
107      local known;
108      if 0 < entry[5] then
109        known:= First( list, x -> x{ [ 1 .. 4 ] } = entry{ [3, 5, 6, 8 ] } );
110        if known = fail then
111          known:= entry{ [ 3, 5, 6, 8 ] };
112          Add( known, [] );
113          Add( list, known );
114        fi;
115        known[5][ entry[10] ]:= name;
116        return true;
117      fi;
118      return false;
119    end,
120
121    DisplayOverviewInfo := [ "#", "r", function( conditions )
122      # Put *all* types of representations together, in particular
123      # assume that the functions for the other "rep" kind types are trivial!
124      local info, no;
125
126      conditions:= ShallowCopy( conditions );
127      conditions[1]:= conditions[1][1];
128      info:= CallFuncList( AllAtlasGeneratingSetInfos, conditions );
129      no:= Length( info );
130      if no = 0 then
131        no:= "";
132      fi;
133      return [ String( no ),
134               not ForAll( info,
135                           x -> IsString( x.identifier[2] )
136                                or ForAll( x.identifier[2], IsString ) ) ];
137    end ],
138
139    AccessGroupCondition := function( info, cond )
140      return  AGR.CheckOneCondition( IsPermGroup, x -> x = true, cond )
141          and AGR.CheckOneCondition( IsPermGroup, cond )
142          and AGR.CheckOneCondition( IsMatrixGroup, x -> x = false, cond )
143          and AGR.CheckOneCondition( NrMovedPoints,
144                  x -> ( IsFunction( x ) and x( info.p ) = true )
145                       or info.p = x, cond )
146          and AGR.CheckOneCondition( IsTransitive,
147                  x -> ( not IsBound( info.transitivity ) and x = fail ) or
148                       ( IsBound( info.transitivity ) and
149                         ( ( IsFunction( x ) and x( info.transitivity > 0 ) = true )
150                           or ( info.transitivity > 0 ) = x ) ), cond )
151          and AGR.CheckOneCondition( Transitivity,
152                  x -> ( not IsBound( info.transitivity ) and x = fail ) or
153                       ( IsBound( info.transitivity ) and
154                         ( ( IsFunction( x ) and x( info.transitivity ) = true )
155                           or info.transitivity = x ) ), cond )
156          and AGR.CheckOneCondition( IsPrimitive,
157                  x -> ( not IsBound( info.isPrimitive ) and x = fail ) or
158                       ( IsBound( info.isPrimitive ) and
159                         ( ( IsFunction( x ) and x( info.isPrimitive ) = true )
160                           or info.isPrimitive = x ) ), cond )
161          and AGR.CheckOneCondition( RankAction,
162                  x -> ( not IsBound( info.rankAction ) and x = fail ) or
163                       ( IsBound( info.rankAction ) and
164                         ( ( IsFunction( x ) and x( info.rankAction ) = true )
165                           or info.rankAction = x ) ), cond )
166          and AGR.CheckOneCondition( Identifier,
167                  x -> ( IsFunction( x ) and x( info.id ) = true )
168                       or info.id = x, cond )
169          and IsEmpty( cond );
170    end,
171
172    DisplayGroup := function( r )
173      local disp, sep;
174
175      if AGR.ShowOnlyASCII() then
176        disp:= Concatenation( "G <= Sym(", String( r.p ), r.id, ")" );
177      else
178        disp:= Concatenation( "G ≤ Sym(", String( r.p ), r.id, ")" );
179      fi;
180      if IsBound( r.transitivity ) then
181        disp:= [ disp ];
182        if   r.transitivity = 0 then
183          # For intransitive repres., show the orbit lengths.
184          Add( disp, Concatenation( "orbit lengths ",
185            JoinStringsWithSeparator( List( r.orbits, String ), ", " ) ) );
186          sep:= ", ";
187        elif r.transitivity = 1 then
188          # For transitivity 1, show the rank (if known).
189          if IsBound( r.rankAction ) and r.rankAction <> "???" then
190            Add( disp, Concatenation( "rank ", String( r.rankAction ) ) );
191            sep:= ", ";
192          fi;
193        elif IsInt( r.transitivity ) then
194          # For transitivity at least 2, show the transitivity.
195          Add( disp, Concatenation( String( r.transitivity ), "-trans." ) );
196          sep:= ", ";
197        else
198          # The transitivity is not known.
199          Add( disp, "" );
200          sep:= "";
201        fi;
202        if 0 < r.transitivity then
203          # For transitive representations, more info may be available.
204          if IsBound( r.isPrimitive ) and r.isPrimitive then
205            if IsBound( r.stabilizer ) and r.stabilizer <> "???" then
206              Add( disp, Concatenation( sep, "on cosets of " ) );
207              Add( disp, r.stabilizer );
208              if IsBound( r.maxnr ) and r.maxnr <> "???" then
209                Add( disp, Concatenation( " (",
210                                          Ordinal( r.maxnr ), " max.)" ) );
211              else
212                Add( disp, "" );
213              fi;
214            elif IsBound( r.maxnr ) and r.maxnr <> "???" then
215              Add( disp, Concatenation( sep, "on cosets of ",
216                                        Ordinal( r.maxnr ), " max." ) );
217            else
218              Add( disp, Concatenation( sep, "primitive" ) );
219            fi;
220          elif IsBound( r.stabilizer ) and r.stabilizer <> "???" then
221            Add( disp, Concatenation( sep, "on cosets of " ) );
222            Add( disp, r.stabilizer );
223          fi;
224        fi;
225      fi;
226      return disp;
227    end,
228
229    TestFileHeaders := function( tocid, groupname, entry, type )
230      local name, cand, filename, len, file, line;
231
232      if tocid = "core" then
233        tocid:= "datagens";
234      fi;
235
236      # Each generator is stored in a file of its own.
237      for name in entry[ Length( entry ) ] do
238
239        # Consider only local files, do not download them.
240        cand:= AtlasOfGroupRepresentationsLocalFilename(
241                   [ [ tocid, name ] ], type );
242        if not ( Length( cand ) = 1 and ForAll( cand[1][2], x -> x[2] ) ) then
243          return true;
244        fi;
245        filename:= cand[1][2][1][1];
246        len:= Length( filename );
247        if 3 < len and filename{ [ len-2 .. len ] } = ".gz" then
248          filename:= filename{ [ 1 .. len-3 ] };
249        fi;
250
251        # Read the first line of the file.
252        file:= InputTextFile( filename );
253        if file = fail then
254          return Concatenation( "cannot create input stream for file `",
255                     filename,"'" );
256        fi;
257        AGR.InfoRead( "#I  reading `",filename,"' started\n" );
258        line:= ReadLine( file );
259        if line = fail then
260          CloseStream( file );
261          return Concatenation( "no first line in file `",filename,"'" );
262        fi;
263        while not '\n' in line do
264          Append( line, ReadLine( file ) );
265        od;
266        CloseStream( file );
267        AGR.InfoRead( "#I  reading `",filename,"' done\n" );
268
269        # The header must consist of four nonnegative integers.
270        line:= CMeatAxeFileHeaderInfo( line );
271        if line = fail then
272          return Concatenation( "illegal header of file `", filename,"'" );
273        fi;
274
275        # Start the specific tests for permutations.
276        # Check mode, number of permutations, and degree.
277        if   line[1] <> 12 then
278          return Concatenation( "mode of file `", name,
279                     "' differs from 12" );
280        elif line[4] <> 1 then
281          return Concatenation(
282                "more than one permutation in file `", name, "'" );
283        elif line[3] <> entry[2] then
284          return Concatenation( "perm. degree in file `",
285                     name, "' is ", String( line[3] ) );
286        fi;
287
288      od;
289      return true;
290    end,
291
292    TestFiles := AGR.TestFilesMTX,
293
294    # Permutation representations are sorted according to
295    # degree and identification string.
296    SortTOCEntries := entry -> entry{ [ 2, 3 ] },
297
298    # Check whether the right number of files is available for each repres.
299    PostprocessFileInfo := function( toc, record )
300      local list, i;
301      list:= record.perm;
302      for i in [ 1 .. Length( list ) ] do
303        if not IsDenseList( list[i][5] ) then
304#T better check whether the number of generators equals the number of
305#T standard generators
306          Info( InfoAtlasRep, 1, "not all generators for ", list[i][5] );
307          Unbind( list[i] );
308        fi;
309      od;
310      if not IsDenseList( list ) then
311        record.perm:= Compacted( list );
312      fi;
313    end,
314
315    # We store the type, the full filename, and the list of CRC values.
316    TOCEntryString := function( typename, entry )
317      local list, pos, name, crc, i, info;
318
319      list:= AtlasOfGroupRepresentationsInfo.filenames;
320      pos:= List( entry[5], nam -> PositionSorted( list, [ nam ] ) );
321      if ForAll( pos, x -> x <= Length( list ) ) and
322         List( pos, x -> list[x][1] ) = entry[5] then
323        name:= list[ pos[1] ][2];
324        crc:= [];
325        for i in [ 1 .. Length( pos ) ] do
326          if Length( list[ pos[i] ] ) < 4 then
327            # There is no crc value yet.
328            # If the file is available locally then compute the value,
329            # otherwise leave it out.
330            info:= AtlasOfGroupRepresentationsLocalFilename(
331                       [ list[ pos[i] ]{ [ 3, 1 ] } ], typename );
332            info:= First( info, l -> l[2][1][2] = true );
333            if info <> fail then
334              crc[i]:= CrcFile( info[2][1][1] );
335            else
336              crc[i]:= fail;
337            fi;
338          else
339            crc[i]:= list[ pos[i] ][4];
340          fi;
341        od;
342        info:= Concatenation( "\"", typename, "\",\"",
343            name{ [ 1 .. PositionSublist( name, ".m" ) + 1 ] }, "\"" );
344        if not fail in crc then
345          Append( info, Concatenation( ",",
346                            ReplacedString( String( crc ), " ", "" ) ) );
347        fi;
348        return info;
349      fi;
350      return fail;
351    end,
352
353    # The default access reads the text format files.
354    # Note that `ScanMeatAxeFile' returns a list of permutations.
355    ReadAndInterpretDefault := paths -> Concatenation( List( paths,
356                                            ScanMeatAxeFile ) ),
357    ) );
358
359
360#############################################################################
361##
362#D  Matrix representations over finite fields
363##
364##  <#GAPDoc Label="type:matff:format">
365##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-f</C><M>q</M><C>r</C><M>dim</M><M>id</M><C>B</C><M>m</M><C>.m</C><M>nr</M></Mark>
366##  <Item>
367##    a file in &MeatAxe; text file format
368##    containing the <M>nr</M>-th generator of a matrix representation
369##    over the field with <M>q</M> elements, of dimension <M>dim</M>.
370##    An example is <C>S5G1-f2r4aB0.m1</C>.
371##  </Item>
372##  <#/GAPDoc>
373##
374AGR.DeclareDataType( "rep", "matff",   rec(
375
376    # `<groupname>G<i>-f<q>r<dim><id>B<m>.m<nr>'
377    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
378                          [ "f", IsDigitChar, "r", IsDigitChar,
379                            AGR.IsLowerAlphaOrDigitChar,
380                            "B", IsDigitChar, ".m", IsDigitChar ] ],
381                        [ ParseBackwards, ParseForwards ] ],
382
383    AddDescribingComponents := function( record, type )
384      local repid, parsed, info, char, pos;
385
386      repid:= record.identifier[2][1];
387      if not IsString( repid ) then
388        repid:= repid[2];
389      fi;
390      parsed:= AGR.ParseFilenameFormat( repid, type[2].FilenameFormat );
391      record.dim:= Int( parsed[7] );
392      record.id:= parsed[8];
393      record.ring:= GF( parsed[5] );
394      if IsBound( AtlasOfGroupRepresentationsInfo.characterinfo.(
395                      record.groupname ) ) then
396        info:= AtlasOfGroupRepresentationsInfo.characterinfo.(
397                   record.groupname );
398        char:= Characteristic( record.ring );
399        if IsBound( info[ char ] ) then
400          info:= info[ char ];
401          pos:= Position( info[2], repid{ [ 1 .. Position( repid, '.' ) - 1 ] } );
402          if pos <> fail then
403            record.constituents:= info[1][ pos ];
404            if IsInt( record.constituents ) then
405              record.constituents:= [ record.constituents ];
406            fi;
407            if info[3][ pos ] <> fail then
408              record.charactername:= info[3][ pos ];
409            fi;
410          fi;
411        fi;
412      fi;
413    end,
414
415    # `[ <i>, <q>, <dim>, <id>, <m>, <filenames> ]'
416    AddFileInfo := function( list, entry, name )
417      local known;
418      if IsPrimePowerInt( entry[5] ) and 0 < entry[7] then
419        known:= First( list, x -> x{ [ 1 .. 5 ] }
420                                  = entry{ [ 3, 5, 7, 8, 10 ] } );
421        if known = fail then
422          known:= entry{ [ 3, 5, 7, 8, 10 ] };
423          Add( known, [] );
424          Add( list, known );
425        fi;
426        known[6][ entry[12] ]:= name;
427        return true;
428      fi;
429      return false;
430    end,
431
432    AccessGroupCondition := function( info, cond )
433      return  AGR.CheckOneCondition( IsMatrixGroup, x -> x = true, cond )
434          and AGR.CheckOneCondition( IsMatrixGroup, cond )
435          and AGR.CheckOneCondition( IsPermGroup, x -> x = false, cond )
436          and AGR.CheckOneCondition( Characteristic,
437                  function( p )
438                    local char;
439                    char:= SmallestRootInt( Size( info.ring ) );
440                    return char = p or IsFunction( p ) and p( char ) = true;
441                  end,
442                  cond )
443          and AGR.CheckOneCondition( Dimension,
444                  x -> ( IsFunction( x ) and x( info.dim ) )
445                       or info.dim = x, cond )
446          and AGR.CheckOneCondition( Ring,
447                  R -> ( IsFunction( R ) and R( info.ring ) ) or
448                       ( IsField( R ) and IsFinite( R )
449                         and Size( info.ring ) mod Characteristic( R ) = 0
450                         and DegreeOverPrimeField( R )
451                            mod LogInt( Size( info.ring ),
452                                        Characteristic( R ) ) = 0 ),
453                  cond )
454          and AGR.CheckOneCondition( Identifier,
455                  x -> ( IsFunction( x ) and x( info.id ) = true )
456                       or info.id = x, cond )
457          and IsEmpty( cond );
458    end,
459
460    DisplayGroup := function( r )
461      local disp;
462
463      if AGR.ShowOnlyASCII() then
464        disp:= Concatenation( "G <= GL(", String( r.dim ), r.id,
465                              ",", String( r.identifier[4] ), ")" );
466        if IsBound( r.charactername ) then
467          disp:= [ disp, Concatenation( "character ", r.charactername ) ];
468        fi;
469      else
470        disp:= Concatenation( "G ≤ GL(", String( r.dim ), r.id,
471                              ",", String( r.identifier[4] ), ")" );
472        if IsBound( r.charactername ) then
473          disp:= [ disp, Concatenation( "φ = ", r.charactername ) ];
474        fi;
475      fi;
476      return disp;
477    end,
478
479    TestFileHeaders := function( tocid, groupname, entry, type )
480      local name, cand, filename, len, file, line, errors;
481
482      if tocid = "core" then
483        tocid:= "datagens";
484      fi;
485
486      # Each generator is stored in a file of its own.
487      for name in entry[ Length( entry ) ] do
488
489        # Consider only local files, do not download them.
490        cand:= AtlasOfGroupRepresentationsLocalFilename(
491                   [ [ tocid, name ] ], type );
492        if not ( Length( cand ) = 1 and ForAll( cand[1][2], x -> x[2] ) ) then
493          return true;
494        fi;
495        filename:= cand[1][2][1][1];
496        len:= Length( filename );
497        if 3 < len and filename{ [ len-2 .. len ] } = ".gz" then
498          filename:= filename{ [ 1 .. len-3 ] };
499        fi;
500
501        # Read the first line of the file.
502        file:= InputTextFile( filename );
503        if file = fail then
504          return Concatenation( "cannot create input stream for file `",
505                     filename,"'" );
506        fi;
507        AGR.InfoRead( "#I  reading `",filename,"' started\n" );
508        line:= ReadLine( file );
509        if line = fail then
510          CloseStream( file );
511          return Concatenation( "no first line in file `",filename,"'" );
512        fi;
513        while not '\n' in line do
514          Append( line, ReadLine( file ) );
515        od;
516        CloseStream( file );
517        AGR.InfoRead( "#I  reading `",filename,"' done\n" );
518
519        # The header must consist of four nonnegative integers.
520        line:= CMeatAxeFileHeaderInfo( line );
521        if line = fail then
522          return Concatenation( "illegal header of file `", filename,"'" );
523        fi;
524
525        # Start the specific tests for matrices over finite fields.
526        # Check mode, field size, and dimension.
527        errors:= "";
528        if   6 < line[1] then
529          Append( errors, Concatenation( "mode of file `", name,
530                            "' is larger than 6" ) );
531        elif line[2] <> entry[2] then
532          Append( errors, Concatenation( "file `", name,
533                            "': field is of size ", String( line[2] ) ) );
534        elif line[3] <> entry[3] then
535          Append( errors, Concatenation( "file `", name,
536                            "': matrix dimension is ", String( line[3] ) ) );
537        elif line[3] <> line[4] then
538          Append( errors, Concatenation( "file `", name,
539                            "': matrix is not square" ) );
540        fi;
541        if not IsEmpty( errors ) then
542          return errors;
543        fi;
544
545      od;
546      return true;
547    end,
548
549    TestFiles := AGR.TestFilesMTX,
550
551    # Matrix representations over finite fields are sorted according to
552    # field size, dimension, and identification string.
553    SortTOCEntries := entry -> entry{ [ 2 .. 4 ] },
554
555    # Check whether the right number of files is available for each repres.
556    PostprocessFileInfo := function( toc, record )
557      local list, i;
558      list:= record.matff;
559      for i in [ 1 .. Length( list ) ] do
560        if not IsDenseList( list[i][6] ) then
561#T better check whether the number of generators equals the number of
562#T standard generators
563          Info( InfoAtlasRep, 1, "not all generators for ", list[i][6] );
564          Unbind( list[i] );
565        fi;
566      od;
567      if not IsDenseList( list ) then
568        record.matff:= Compacted( list );
569      fi;
570    end,
571
572    # We store the type, the full filename, and the list of CRC values.
573    TOCEntryString := function( typename, entry )
574      local list, pos, name, crc, i, info;
575
576      list:= AtlasOfGroupRepresentationsInfo.filenames;
577      pos:= List( entry[6], nam -> PositionSorted( list, [ nam ] ) );
578      if ForAll( pos, x -> x <= Length( list ) ) and
579         List( pos, x -> list[x][1] ) = entry[6] then
580        name:= list[ pos[1] ][2];
581        crc:= [];
582        for i in [ 1 .. Length( pos ) ] do
583          if Length( list[ pos[i] ] ) < 4 then
584            # There is no crc value yet.
585            # If the file is available locally then compute the value,
586            # otherwise leave it out.
587            info:= AtlasOfGroupRepresentationsLocalFilename(
588                       [ list[ pos[i] ]{ [ 3, 1 ] } ], typename );
589            info:= First( info, l -> l[2][1][2] = true );
590            if info <> fail then
591              crc[i]:= CrcFile( info[2][1][1] );
592            else
593              crc[i]:= fail;
594            fi;
595          else
596            crc[i]:= list[ pos[i] ][4];
597          fi;
598        od;
599        info:= Concatenation( "\"", typename, "\",\"",
600            name{ [ 1 .. PositionSublist( name, ".m" ) + 1 ] }, "\"" );
601        if not fail in crc then
602          Append( info, Concatenation( ",",
603                            ReplacedString( String( crc ), " ", "" ) ) );
604        fi;
605        return info;
606      fi;
607      return fail;
608    end,
609
610    # The default access reads the text format files.
611    ReadAndInterpretDefault := paths -> List( paths, ScanMeatAxeFile ),
612    ) );
613
614
615#############################################################################
616##
617#D  Matrix representations over the integers
618##
619##  <#GAPDoc Label="type:matint:format">
620##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-Zr</C><M>dim</M><M>id</M><C>B</C><M>m</M>.g</Mark>
621##  <Item>
622##    a &GAP; readable file
623##    containing all generators of a matrix representation
624##    over the integers, of dimension <M>dim</M>.
625##    An example is <C>A5G1-Zr4B0.g</C>.
626##  </Item>
627##  <#/GAPDoc>
628##
629AGR.DeclareDataType( "rep", "matint",  rec(
630
631    # `<groupname>G<i>-Zr<dim><id>B<m>.g'
632    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
633                        [ "Zr", IsDigitChar, AGR.IsLowerAlphaOrDigitChar,
634                          "B", IsDigitChar, ".g" ] ],
635                        [ ParseBackwards, ParseForwards ] ],
636
637    AddDescribingComponents := function( record, type )
638      local repid, parsed, info, pos;
639
640      repid:= record.identifier[2];
641      if not IsString( repid ) then
642        # one private file
643        repid:= repid[1][2];
644      fi;
645      parsed:= AGR.ParseFilenameFormat( repid, type[2].FilenameFormat );
646      record.dim:= Int( parsed[5] );
647      record.id:= parsed[6];
648      record.ring:= Integers;
649      if IsBound( AtlasOfGroupRepresentationsInfo.characterinfo.(
650                      record.groupname ) ) then
651        info:= AtlasOfGroupRepresentationsInfo.characterinfo.(
652                   record.groupname );
653        if IsBound( info[1] ) then
654          info:= info[1];
655          pos:= Position( info[2], repid{ [ 1 .. Position( repid, '.' ) - 1 ] } );
656          if pos <> fail then
657            record.constituents:= info[1][ pos ];
658            if IsInt( record.constituents ) then
659              record.constituents:= [ record.constituents ];
660            fi;
661            if info[3][ pos ] <> fail then
662              record.charactername:= info[3][ pos ];
663            fi;
664          fi;
665        fi;
666      fi;
667    end,
668
669    # `[ <i>, <dim>, <id>, <m>, <filename> ]'
670    AddFileInfo := function( list, entry, name )
671      if 0 < entry[5] then
672        Add( list, Concatenation( entry{ [ 3, 5, 6, 8 ] }, [ name ] ) );
673        return true;
674      fi;
675      return false;
676    end,
677
678    AccessGroupCondition := function( info, cond )
679      return  AGR.CheckOneCondition( IsMatrixGroup, x -> x = true, cond )
680          and AGR.CheckOneCondition( IsMatrixGroup, cond )
681          and AGR.CheckOneCondition( IsPermGroup, x -> x = false, cond )
682          and AGR.CheckOneCondition( Characteristic,
683                  p -> p = 0 or ( IsFunction( p ) and p( 0 ) = true ),
684                  cond )
685          and AGR.CheckOneCondition( Dimension,
686                  x -> ( IsFunction( x ) and x( info.dim ) )
687                       or info.dim = x, cond )
688          and AGR.CheckOneCondition( Ring,
689                  R -> ( IsFunction( R ) and R( Integers ) ) or
690                       ( IsRing( R ) and IsCyclotomicCollection( R ) ), cond )
691          and AGR.CheckOneCondition( Identifier,
692                  x -> ( IsFunction( x ) and x( info.id ) = true )
693                       or info.id = x, cond )
694          and IsEmpty( cond );
695    end,
696
697    TestFileHeaders := function( tocid, groupname, entry, type )
698      return AGR.TestFileHeadersDefault( tocid, groupname, entry, type,
699               entry[2],
700               function( entry, mats, filename )
701                 if not ForAll( mats, mat -> ForAll( mat,
702                                 row -> ForAll( row, IsInt ) ) ) then
703                   return Concatenation( "matrices in `", filename,
704                              "' are not over the integers" );
705                 fi;
706                 return true;
707               end );
708    end,
709
710    DisplayGroup := function( r )
711      local disp;
712
713      if AGR.ShowOnlyASCII() then
714        disp:= Concatenation( "G <= GL(", String( r.dim ), r.id, ",Z)" );
715        if IsBound( r.charactername ) then
716          disp:= [ disp, Concatenation( "character ", r.charactername ) ];
717        fi;
718      else
719        disp:= Concatenation( "G ≤ GL(", String( r.dim ), r.id, ",ℤ)" );
720        if IsBound( r.charactername ) then
721          disp:= [ disp, Concatenation( "χ = ", r.charactername ) ];
722        fi;
723      fi;
724      return disp;
725    end,
726
727    # Matrix representations over the integers are sorted according to
728    # dimension and identification string.
729    SortTOCEntries := entry -> entry{ [ 2, 3 ] },
730
731    # There is only one file.
732    ReadAndInterpretDefault := paths -> AtlasDataGAPFormatFile(
733                                            paths[1] ).generators,
734    ) );
735
736
737#############################################################################
738##
739#D  Matrix representations over algebraic number fields
740##
741##  <#GAPDoc Label="type:matalg:format">
742##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-Ar</C><M>dim</M><M>id</M><C>B</C><M>m</M><C>.g</C></Mark>
743##  <Item>
744##    a &GAP; readable file
745##    containing all generators of a matrix representation of dimension
746##    <M>dim</M> over an algebraic number field not specified further.
747##    An example is <C>A5G1-Ar3aB0.g</C>.
748##  </Item>
749##  <#/GAPDoc>
750##
751AGR.DeclareDataType( "rep", "matalg",  rec(
752
753    # `<groupname>G<i>-Ar<dim><id>B<m>.g'
754    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
755                        [ "Ar", IsDigitChar, AGR.IsLowerAlphaOrDigitChar,
756                          "B", IsDigitChar, ".g" ] ],
757                        [ ParseBackwards, ParseForwards ] ],
758
759    AddDescribingComponents := function( record, type )
760      local repid, parsed, info, pos;
761
762      repid:= record.identifier[2];
763      if not IsString( repid ) then
764        # one private file
765        repid:= repid[1][2];
766      fi;
767      parsed:= AGR.ParseFilenameFormat( repid, type[2].FilenameFormat );
768      record.dim:= Int( parsed[5] );
769      record.id:= parsed[6];
770      info:= repid{ [ 1 .. Position( repid, '.' )-1 ] };
771      info:= First( AtlasOfGroupRepresentationsInfo.ringinfo,
772                    x -> x[1] = info );
773      if info <> fail then
774        record.ring:= info[3];
775      fi;
776      if IsBound( AtlasOfGroupRepresentationsInfo.characterinfo.(
777                      record.groupname ) ) then
778        info:= AtlasOfGroupRepresentationsInfo.characterinfo.(
779                   record.groupname );
780        if IsBound( info[1] ) then
781          info:= info[1];
782          pos:= Position( info[2], repid{ [ 1 .. Position( repid, '.' ) - 1 ] } );
783          if pos <> fail then
784            record.constituents:= info[1][ pos ];
785            if IsInt( record.constituents ) then
786              record.constituents:= [ record.constituents ];
787            fi;
788            if info[3][ pos ] <> fail then
789              record.charactername:= info[3][ pos ];
790            fi;
791          fi;
792        fi;
793      fi;
794    end,
795
796    # `[ <i>, <dim>, <id>, <m>, <filename> ]'
797    AddFileInfo := function( list, entry, name )
798      if 0 < entry[5] then
799        Add( list, Concatenation( entry{ [ 3, 5, 6, 8 ] }, [ name ] ) );
800        return true;
801      fi;
802      return false;
803    end,
804
805    AccessGroupCondition := function( info, cond )
806      return  AGR.CheckOneCondition( IsMatrixGroup, x -> x = true, cond )
807          and AGR.CheckOneCondition( IsMatrixGroup, cond )
808          and AGR.CheckOneCondition( IsPermGroup, x -> x = false, cond )
809          and AGR.CheckOneCondition( Characteristic,
810                  p -> p = 0 or ( IsFunction( p ) and p( 0 ) = true ),
811                  cond )
812          and AGR.CheckOneCondition( Dimension,
813                  x -> ( IsFunction( x ) and x( info.dim ) = true )
814                       or info.dim = x, cond )
815          and AGR.CheckOneCondition( Ring,
816                  x -> IsIdenticalObj( x, Cyclotomics ) or
817                       ( not IsBound( info.ring ) and x = fail ) or
818                       ( IsBound( info.ring ) and
819                         ( ( IsFunction( x ) and x( info.ring ) = true )
820                           or ( IsRing( x ) and IsCyclotomicCollection( x )
821#T problem with GAP:
822#T 'IsSubset( Integers, CF(5) )' runs into an error
823                                and ( not IsIdenticalObj( x, Integers ) and
824                                      IsSubset( x, info.ring ) ) ) ) ), cond )
825          and AGR.CheckOneCondition( Identifier,
826                  x -> ( IsFunction( x ) and x( info.id ) = true )
827                       or info.id = x, cond )
828          and IsEmpty( cond );
829    end,
830
831    TestFileHeaders := function( tocid, groupname, entry, type )
832      return AGR.TestFileHeadersDefault( tocid, groupname, entry, type,
833               entry[2],
834               function( entry, mats, filename )
835                 local info;
836
837                 if not IsCyclotomicCollCollColl( mats ) then
838                   return Concatenation( "matrices in `",filename,
839                              "' are not over cyclotomics" );
840                 elif ForAll( Flat( mats ), IsInt ) then
841                   return Concatenation( "matrices in `",filename,
842                              "' are over the integers" );
843                 fi;
844                 filename:= filename{ [ 1 .. Position( filename, '.' )-1 ] };
845                 info:= First( AtlasOfGroupRepresentationsInfo.ringinfo,
846                               triple -> triple[1] = filename );
847                 if info = fail then
848                   return Concatenation( "field info for `",filename,
849                              "' missing" );
850                 elif Field( Rationals, Flat( mats ) ) <> info[3] then
851                   return Concatenation( "field info for `",filename,
852                              "' should be ",
853                              String( Field( Rationals, Flat( mats ) ) ) );
854                 fi;
855                 return true;
856               end );
857    end,
858
859    DisplayGroup := function( r )
860      local fld, disp;
861
862      fld:= r.identifier[2];
863      if not IsString( fld ) then
864        fld:= fld[1][2];
865      fi;
866      fld:= fld{ [ 1 .. Length( fld )-2 ] };
867      fld:= First( AtlasOfGroupRepresentationsInfo.ringinfo,
868                   p -> p[1] = fld );
869      if AGR.ShowOnlyASCII() then
870        if fld <> fail then
871          fld:= fld[2];
872        else
873          fld:= "C";
874        fi;
875        disp:= Concatenation( "G <= GL(", String( r.dim ), r.id, ",",
876                              fld, ")" );
877        if IsBound( r.charactername ) then
878          disp:= [ disp, Concatenation( "character ", r.charactername ) ];
879        fi;
880      else
881        if fld <> fail then
882          fld:= fld[2];
883        else
884          fld:= "ℂ";
885        fi;
886        disp:= Concatenation( "G ≤ GL(", String( r.dim ), r.id, ",",
887                              fld, ")" );
888        if IsBound( r.charactername ) then
889          disp:= [ disp, Concatenation( "χ = ", r.charactername ) ];
890        fi;
891      fi;
892      return disp;
893    end,
894
895    # Matrix representations over algebraic extension fields are sorted
896    # according to dimension and identification string.
897    SortTOCEntries := entry -> entry{ [ 2, 3 ] },
898
899    # There is only one file.
900    ReadAndInterpretDefault := paths -> AtlasDataGAPFormatFile(
901                                            paths[1] ).generators,
902    ) );
903
904
905#############################################################################
906##
907#D  Matrix representations over residue class rings
908##
909##  <#GAPDoc Label="type:matmodn:format">
910##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-Z</C><M>n</M><C>r</C><M>dim</M><M>id</M><C>B</C><M>m</M><C>.g</C></Mark>
911##  <Item>
912##    a &GAP; readable file
913##    containing all generators of a matrix representation of dimension
914##    <M>dim</M> over the ring of integers mod <M>n</M>.
915##    An example is <C>2A8G1-Z4r4aB0.g</C>.
916##  </Item>
917##  <#/GAPDoc>
918##
919AGR.DeclareDataType( "rep", "matmodn", rec(
920
921    # `<groupname>G<i>-Z<n>r<dim><id>B<m>.g'
922    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
923                          [ "Z", IsDigitChar, "r", IsDigitChar,
924                            AGR.IsLowerAlphaOrDigitChar,
925                            "B", IsDigitChar, ".g" ] ],
926                        [ ParseBackwards, ParseForwards ] ],
927
928    AddDescribingComponents := function( record, type )
929      local repid, parsed;
930
931      repid:= record.identifier[2];
932      if not IsString( repid ) then
933        # one private file
934        repid:= repid[1][2];
935      fi;
936      parsed:= AGR.ParseFilenameFormat( repid, type[2].FilenameFormat );
937      record.dim:= Int( parsed[7] );
938      record.id:= parsed[8];
939      record.ring:= ZmodnZ( parsed[5] );
940    end,
941
942    # `[ <i>, <n>, <dim>, <id>, <m>, <filename> ]'
943    AddFileInfo := function( list, entry, name )
944      if 0 < entry[5] and 0 < entry[7] then
945        Add( list, Concatenation( entry{ [ 3, 5, 7, 8, 10 ] }, [ name ] ) );
946        return true;
947      fi;
948      return false;
949    end,
950
951    AccessGroupCondition := function( info, cond )
952      return  AGR.CheckOneCondition( IsMatrixGroup, x -> x = true, cond )
953          and AGR.CheckOneCondition( IsMatrixGroup, cond )
954          and AGR.CheckOneCondition( IsPermGroup, x -> x = false, cond )
955          and AGR.CheckOneCondition( Characteristic,
956                  p -> p = fail or ( IsFunction( p ) and p( fail ) = true ),
957                  cond )
958          and AGR.CheckOneCondition( Dimension,
959                  x -> ( IsFunction( x ) and x( info.dim ) )
960                       or info.dim = x, cond )
961          and AGR.CheckOneCondition( Ring,
962                  R -> ( IsFunction( R ) and R( info.ring ) ) or
963                       ( IsRing( R )
964                  and IsZmodnZObjNonprimeCollection( R )
965                  and ModulusOfZmodnZObj( One( R ) ) = Size( info.ring ) ),
966                  cond )
967          and AGR.CheckOneCondition( Identifier,
968                  x -> ( IsFunction( x ) and x( info.id ) = true )
969                       or info.id = x, cond )
970          and IsEmpty( cond );
971    end,
972
973    DisplayGroup := function( r )
974      if AGR.ShowOnlyASCII() then
975        return Concatenation( "G <= GL(",String( r.dim ), r.id,
976                              ",Z/", String( r.identifier[4] ),"Z)" );
977      else
978        return Concatenation( "G ≤ GL(",String( r.dim ), r.id,
979                              ",ℤ/", String( r.identifier[4] ),"ℤ)" );
980      fi;
981    end,
982
983    TestFileHeaders := function( tocid, groupname, entry, type )
984      return AGR.TestFileHeadersDefault( tocid, groupname, entry, type,
985               entry[3],
986               function( entry, mats, filename )
987                 if   not IsZmodnZObjNonprimeCollCollColl( mats ) then
988                   return Concatenation( "matrices in `", filename,
989                              "' are not over a residue class ring" );
990                 elif ModulusOfZmodnZObj( mats[1][1][1] ) <> entry[2] then
991                   return Concatenation( "matrices in `", filename,
992                              "' are not over Z/", entry[2], "Z" );
993                 fi;
994                 return true;
995               end );
996    end,
997
998    # Matrix representations over residue class rings are sorted according
999    # to modulus, dimension, and identification string.
1000    SortTOCEntries := entry -> entry{ [ 2 .. 4 ] },
1001
1002    # There is only one file.
1003    ReadAndInterpretDefault := paths -> AtlasDataGAPFormatFile(
1004                                            paths[1] ).generators,
1005    ) );
1006
1007
1008#############################################################################
1009##
1010#D  Quaternionic matrix representations
1011##
1012##  <#GAPDoc Label="type:quat:format">
1013##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-Hr</C><M>dim</M><M>id</M><C>B</C><M>m</M><C>.g</C></Mark>
1014##  <Item>
1015##    a &GAP; readable file
1016##    containing all generators of a matrix representation
1017##    over a quaternion algebra over an algebraic number field,
1018##    of dimension <M>dim</M>.
1019##    An example is <C>2A6G1-Hr2aB0.g</C>.
1020##  </Item>
1021##  <#/GAPDoc>
1022##
1023AGR.DeclareDataType( "rep", "quat",  rec(
1024
1025    # `<groupname>G<i>-Hr<dim><id>B<m>.g'
1026    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
1027                          [ "Hr", IsDigitChar, AGR.IsLowerAlphaOrDigitChar,
1028                            "B", IsDigitChar, ".g" ] ],
1029                        [ ParseBackwards, ParseForwards ] ],
1030
1031    AddDescribingComponents := function( record, type )
1032      local repid, parsed, info;
1033
1034      repid:= record.identifier[2];
1035      if not IsString( repid ) then
1036        # one private file
1037        repid:= repid[1][2];
1038      fi;
1039      parsed:= AGR.ParseFilenameFormat( repid, type[2].FilenameFormat );
1040      record.dim:= Int( parsed[5] );
1041      record.id:= parsed[6];
1042      info:= repid{ [ 1 .. Position( repid, '.' )-1 ] };
1043      info:= First( AtlasOfGroupRepresentationsInfo.ringinfo,
1044                    x -> x[1] = info );
1045      if info <> fail then
1046        record.ring:= info[3];
1047      fi;
1048    end,
1049
1050    # `[ <i>, <dim>, <id>, <m>, <filename> ]'
1051    AddFileInfo := function( list, entry, name )
1052      if 0 < entry[5] then
1053        Add( list, Concatenation( entry{ [ 3, 5, 6, 8 ] }, [ name ] ) );
1054        return true;
1055      fi;
1056      return false;
1057    end,
1058
1059    AccessGroupCondition := function( info, cond )
1060      return  AGR.CheckOneCondition( IsMatrixGroup, x -> x = true, cond )
1061          and AGR.CheckOneCondition( IsMatrixGroup, cond )
1062          and AGR.CheckOneCondition( IsPermGroup, x -> x = false, cond )
1063          and AGR.CheckOneCondition( Characteristic,
1064                  p -> p = 0 or ( IsFunction( p ) and p( 0 ) = true ),
1065                  cond )
1066          and AGR.CheckOneCondition( Dimension,
1067                  x -> ( IsFunction( x ) and x( info.dim ) = true )
1068                       or info.dim = x, cond )
1069          and AGR.CheckOneCondition( Ring,
1070                  x -> ( not IsBound( info.ring ) and x = fail ) or
1071                       ( IsBound( info.ring ) and
1072                         ( ( IsFunction( x ) and x( info.ring ) = true )
1073                           or ( IsRing( x ) and IsQuaternionCollection( x )
1074                                and IsSubset( x, info.ring ) ) ) ), cond )
1075          and AGR.CheckOneCondition( Identifier,
1076                  x -> ( IsFunction( x ) and x( info.id ) = true )
1077                       or info.id = x, cond )
1078          and IsEmpty( cond );
1079    end,
1080
1081    TestFileHeaders := function( tocid, groupname, entry, type )
1082      return AGR.TestFileHeadersDefault( tocid, groupname, entry, type,
1083               entry[2],
1084               function( entry, mats, filename )
1085                 local info;
1086
1087                 if not ForAll( mats, IsQuaternionCollColl ) then
1088                   return Concatenation( "matrices in `",filename,
1089                              "' are not over the quaternions" );
1090                 fi;
1091                 filename:= filename{ [ 1 .. Position( filename, '.' )-1 ] };
1092                 info:= First( AtlasOfGroupRepresentationsInfo.ringinfo,
1093                               triple -> triple[1] = filename );
1094                 if info = fail then
1095                   return Concatenation( "field info for `",filename,
1096                              "' missing" );
1097                 elif Field( Flat( List( Flat( mats ), ExtRepOfObj ) ) )
1098                      <> EvalString( Concatenation( "Field",
1099                             info[2]{ [ Position( info[2], '(' ) ..
1100                                      Length( info[2] ) ] } ) ) then
1101                   return Concatenation( "field info for `", filename,
1102                              "' should involve ",
1103                              Field( Flat( List( Flat( mats ),
1104                                                 ExtRepOfObj ) ) ) );
1105                 fi;
1106                 return true;
1107               end );
1108    end,
1109
1110    DisplayGroup := function( r )
1111      local fld;
1112
1113      fld:= r.identifier[2];
1114      if not IsString( fld ) then
1115        fld:= fld[1][2];
1116      fi;
1117      fld:= fld{ [ 1 .. Length( fld )-2 ] };
1118      fld:= First( AtlasOfGroupRepresentationsInfo.ringinfo,
1119                   p -> p[1] = fld );
1120      if AGR.ShowOnlyASCII() then
1121        if fld = fail then
1122          fld:= "QuaternionAlgebra(C)";
1123        else
1124          fld:= fld[2];
1125        fi;
1126        return Concatenation( "G <= GL(", String( r.dim ), r.id, ",", fld,
1127                              ")" );
1128      else
1129        if fld = fail then
1130          fld:= "QuaternionAlgebra(ℂ)";
1131        else
1132          fld:= fld[2];
1133        fi;
1134        return Concatenation( "G ≤ GL(", String( r.dim ), r.id, ",", fld,
1135                              ")" );
1136      fi;
1137    end,
1138
1139    # Matrix representations over the quaternions are sorted according to
1140    # dimension and identification string.
1141    SortTOCEntries := entry -> entry{ [ 2, 3 ] },
1142
1143    # There is only one file.
1144    ReadAndInterpretDefault := paths -> AtlasDataGAPFormatFile(
1145                                            paths[1] ).generators,
1146    ) );
1147
1148
1149#############################################################################
1150##
1151#D  Straight line programs for generators of maximal subgroups
1152##
1153##  <#GAPDoc Label="type:maxes:format">
1154##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-max</C><M>k</M><C>W</C><M>n</M></Mark>
1155##  <Item>
1156##    In this case, the file contains a straight line program that takes
1157##    generators of <M>G</M> w.&nbsp;r.&nbsp;t.&nbsp;the <M>i</M>-th set
1158##    of standard generators,
1159##    and returns a list of generators
1160##    (in general <E>not</E> standard generators)
1161##    for a subgroup <M>U</M> in the <M>k</M>-th class of maximal subgroups
1162##    of <M>G</M>.
1163##    An example is <C>J1G1-max7W1</C>.
1164##  </Item>
1165##  <#/GAPDoc>
1166##
1167AGR.DeclareDataType( "prg", "maxes", rec(
1168
1169    # `<groupname>G<i>-max<k>W<n>'
1170    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
1171                          [ "max", IsDigitChar, "W", IsDigitChar ] ],
1172                        [ ParseBackwards, ParseForwards ] ],
1173
1174    # `[ <i>, <k>, <filename> ]'
1175    AddFileInfo := function( list, entry, name )
1176      if 0 < entry[5] then
1177        Add( list, Concatenation( entry{ [ 3, 5 ] }, [ name ] ) );
1178        return true;
1179      fi;
1180      return false;
1181    end,
1182
1183    DisplayOverviewInfo := [ "maxes", "r", function( conditions )
1184      local groupname, tocs, std, info, factgroupinfo, maxext, value,
1185            private, toc, record, new, finfo, factgroupname;
1186
1187      groupname:= conditions[1][2];
1188      tocs:= AGR.TablesOfContents( conditions );
1189      if Length( conditions ) = 1 or
1190         not ( IsInt( conditions[2] ) or IsList( conditions[2] ) ) then
1191        std:= true;
1192      else
1193        std:= conditions[2];
1194        if IsInt( std ) then
1195          std:= [ std ];
1196        fi;
1197      fi;
1198
1199      info:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
1200                    x -> x[2] = groupname );
1201      if info = fail or not IsBound( info[3].maxext )
1202                     or not IsBound( info[3].factorCompatibility ) then
1203        factgroupinfo:= [];
1204        maxext:= [];
1205      else
1206        factgroupinfo:= Filtered( info[3].factorCompatibility,
1207                                  x -> ( std = true or x[1] in std )
1208                                       and x[4] = true );
1209        maxext:= Filtered( info[3].maxext,
1210                            x -> std = true or x[1] in std );
1211      fi;
1212
1213      value:= [];
1214      private:= false;
1215      for toc in tocs do
1216        # If a straight line program for the restriction is available
1217        # then take it.
1218        if IsBound( toc.( groupname ) ) then
1219          record:= toc.( groupname );
1220          if IsBound( record.maxes ) then
1221            new:= List( Filtered( record.maxes,
1222                                  x -> std = true or x[1] in std ),
1223                        x -> x[2] );
1224            if toc.TocID <> "core" and not IsEmpty( new ) then
1225              private:= true;
1226            fi;
1227            UniteSet( value, new );
1228          fi;
1229        fi;
1230
1231        # If a straight line program is available for the restriction
1232        # to the maximal subgroup of a factor group,
1233        # and if this program can be used also here
1234        # then take it.
1235        for finfo in factgroupinfo do
1236          factgroupname:= finfo[2];
1237          info:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
1238                        x -> x[1] = factgroupname );
1239          factgroupname:= info[2];
1240          if IsBound( toc.( factgroupname ) ) then
1241            record:= toc.( factgroupname );
1242            if IsBound( record.maxes ) then
1243              new:= List( Filtered( maxext,
1244                                    l -> ForAny( record.maxes,
1245                                                 fl -> fl[3] = l[3][1] ) ),
1246                          x -> x[2] );
1247              if toc.TocID <> "core" and not IsEmpty( new ) then
1248                private:= true;
1249              fi;
1250              UniteSet( value, new );
1251            fi;
1252          fi;
1253        od;
1254      od;
1255      if IsEmpty( value ) then
1256        value:= "";
1257      else
1258        value:= String( Length( value ) );
1259      fi;
1260      return [ value, private ];
1261    end ],
1262
1263    DisplayPRG := function( tocs, names, std, stdavail )
1264      local data, sortkeys, alltocs, info,
1265            factgroupinfo, maxext, prvwidth, toc, record, i, private, mxstd,
1266            pos, finfo, factgroupname, facti, ker, kerid, pi, result, nrmaxes,
1267            title, entry, line, width, prvphantom, maxnr, j, line2;
1268
1269      data:= [];
1270      sortkeys:= [];
1271
1272      alltocs := AGR.TablesOfContents( "all" );
1273      info:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
1274                    x -> x[2] = names[2] );
1275      if info = fail or not IsBound( info[3].maxext )
1276                     or not IsBound( info[3].factorCompatibility ) then
1277        factgroupinfo:= [];
1278        maxext:= [];
1279      else
1280        factgroupinfo:= Filtered( info[3].factorCompatibility,
1281                                  x -> ( std = true or x[1] in std )
1282                                       and x[4] = true );
1283        maxext:= Filtered( info[3].maxext,
1284                            x -> std = true or x[1] in std );
1285      fi;
1286
1287      prvwidth:= 0;
1288      for toc in tocs do
1289        # If a straight line program for the restriction is available
1290        # then take it.
1291        if IsBound( toc.( names[2] ) ) then
1292          record:= toc.( names[2] );
1293          if IsBound( record.maxes ) then
1294            for i in record.maxes do
1295              if std = true or i[1] in std then
1296                if toc.TocID <> "core" then
1297                  private:= UserPreference( "AtlasRep",
1298                                "AtlasRepMarkNonCoreData" );
1299                  prvwidth:= Length( private );
1300                else
1301                  private := "";
1302                fi;
1303
1304                entry:= [ ,
1305                          private,
1306                          String( i[1] ),
1307                          AGR.VersionOfSLP( i[3] ),
1308                          [ names[1], i[3], i[1] ] ];
1309                if toc.TocID <> "core" then
1310                  entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
1311                fi;
1312
1313                # If *standard* generators of the max. subgroup are available
1314                # (perhaps in another table of contents) then mention this,
1315                # in a line of its own;
1316                # note that Browse will allow one to click on the line.
1317                mxstd:= AGR.StandardizeMaximalSubgroup( names[2], i[3], true,
1318                                                        true );
1319                Add( data, entry );
1320                Add( sortkeys, [ i[2], i[1], Int( entry[4] ) ] );
1321                if mxstd <> fail then
1322                  entry:= ShallowCopy( entry );
1323                  entry[5]:= ShallowCopy( entry[5] );
1324                  if IsString( entry[5][2] ) then
1325                    entry[5][2]:= [ entry[5][2], mxstd[1] ];
1326                  else
1327                    Add( entry[5][2], mxstd[1] );
1328                  fi;
1329                  entry[6]:= Concatenation( "std. ", String( mxstd[2] ) );
1330                  Add( data, entry );
1331                  Add( sortkeys, [ i[2], i[1], Int( entry[4] ), mxstd[2] ] );
1332                fi;
1333              fi;
1334            od;
1335          fi;
1336        fi;
1337
1338        # If a straight line program is available for the restriction
1339        # to the maximal subgroup of a factor group,
1340        # and if this program can be used also here
1341        # then take it.
1342        for finfo in factgroupinfo do
1343          factgroupname:= finfo[2];
1344          info:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
1345                        x -> x[1] = factgroupname );
1346          factgroupname:= info[2];
1347          if IsBound( toc.( factgroupname ) ) then
1348            record:= toc.( factgroupname );
1349            if IsBound( record.maxes ) then
1350              for i in maxext do
1351                facti:= First( record.maxes, fl -> fl[3] = i[3][1] );
1352                if facti <> fail and ( std = true or i[1] in std ) then
1353                  if toc.TocID <> "core" then
1354                    private:= UserPreference( "AtlasRep",
1355                                  "AtlasRepMarkNonCoreData" );
1356                    prvwidth:= Length( private );
1357                  else
1358                    private := "";
1359                  fi;
1360
1361                  entry:= [ ,
1362                            private,
1363                            String( i[1] ),
1364                            AGR.VersionOfSLP( facti[3] ),
1365                            [ names[1], i[3], i[1] ] ];
1366                  if Length( i[3] ) = 1 then
1367                    entry[5][2]:= i[3][1];
1368                    # No additional kernel generators are needed.
1369                    if toc.TocID <> "core" then
1370                      entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
1371                    fi;
1372                  else
1373                    # We have to specify a program for computing the kernel.
1374                    ker:= AtlasProgramInfo( names[1], std, "kernel", finfo[2] );
1375                    if ker <> fail then
1376                      kerid:= ker.identifier[2];
1377                      if IsString( kerid ) then
1378                        entry[5][2]:= [ entry[5][2][1], kerid ];
1379                      else
1380                        entry[5][2]:= [ entry[5][2][1], kerid[1] ];
1381                      fi;
1382                      if toc.TocID <> "core" then
1383                        entry[5][2][1]:= [ toc.TocID, entry[5][2][1] ];
1384                      fi;
1385                    fi;
1386                  fi;
1387                  Add( data, entry );
1388                  Add( sortkeys, [ i[2], i[1], Int( entry[4] ) ] );
1389                fi;
1390              od;
1391            fi;
1392          fi;
1393        od;
1394      od;
1395
1396      title:= "maxes";
1397      if not IsEmpty( data ) then
1398        entry:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
1399                       x -> x[2] = names[2] );
1400        if IsBound( entry[3].nrMaxes ) then
1401          title:= "maxes (";
1402          nrmaxes:= Length( Set( List( sortkeys, x -> x[1] ) ) );
1403          if nrmaxes = entry[3].nrMaxes then
1404            Append( title, "all " );
1405          else
1406            Append( title, String( nrmaxes ) );
1407            Append( title, " out of " );
1408          fi;
1409          Append( title, String( entry[3].nrMaxes ) );
1410          Append( title, ")" );
1411        fi;
1412
1413        SortParallel( sortkeys, data );
1414
1415        width:= Length( String( sortkeys[ Length( sortkeys ) ][1] ) );
1416        prvphantom:= RepeatedString( " ", prvwidth );
1417
1418        for i in [ 1 .. Length( data ) ] do
1419          # Compute the value for the first column (including privacy flag).
1420          maxnr:= sortkeys[i][1];
1421          line:= String( maxnr, width );
1422          Append( line, data[i][2] );
1423          if IsBound( entry[3].structureMaxes ) and
1424             IsBound( entry[3].structureMaxes[ maxnr ] ) then
1425            Append( line, ":  " );
1426            if data[i][2] = "" then
1427              Append( line, prvphantom );
1428            fi;
1429            Append( line, entry[3].structureMaxes[ maxnr ] );
1430          fi;
1431          data[i][1]:= line;
1432          data[i][2]:= "";
1433        od;
1434      fi;
1435
1436      return AGR.CommonDisplayPRG( title, stdavail, data, false );
1437    end,
1438
1439    # Create the program info from the identifier.
1440    AtlasProgramInfo := function( type, identifier, groupname )
1441      local filename, i, result, gapname;
1442
1443      # We need only the information about the restriction part,
1444      # not a standardization or kernel generators.
1445      filename:= identifier[2];
1446      if not IsString( filename ) then
1447        if IsString( filename[1] ) then
1448          filename:= filename[1];
1449        else
1450          filename:= filename[1][2];
1451        fi;
1452      fi;
1453
1454      i:= AGR.ParseFilenameFormat( filename, type[2].FilenameFormat );
1455      if i = fail then
1456        return fail;
1457      fi;
1458      i:= i[5];
1459
1460      result:= rec( standardization := identifier[3],
1461                    identifier      := identifier );
1462
1463      # Set the size if available.
1464      gapname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
1465                       pair -> pair[2] = groupname );
1466      if IsBound( gapname[3].sizesMaxes )
1467         and IsBound( gapname[3].sizesMaxes[i] ) then
1468        result.size:= gapname[3].sizesMaxes[i];
1469      fi;
1470      if IsBound( gapname[3].structureMaxes ) and
1471         IsBound( gapname[3].structureMaxes[i] ) then
1472        result.subgroupname:= gapname[3].structureMaxes[i];
1473      fi;
1474
1475      return result;
1476    end,
1477
1478    # Create the program from the identifier.
1479    AtlasProgram := function( type, identifier, groupname )
1480      local i, std, result, entry, prog, names, pos, maxstd, info, datadirs,
1481            name, kerprg, gapname;
1482
1483      i:= identifier[2];
1484      if not IsString( i ) then
1485        i:= i[1];
1486        if not IsString( i ) then
1487          i:= i[2];
1488        fi;
1489      fi;
1490      i:= AGR.ParseFilenameFormat( i, type[2].FilenameFormat );
1491      if i = fail then
1492        return fail;
1493      fi;
1494      i:= i[5];
1495      std:= identifier[3];
1496
1497      # The second entry is one of
1498      # - the filename of the program,
1499      # - this filename plus a filename for standardization
1500      #   (so we need the *composition* of two programs).
1501      # - this filename plus a filename for kernel generators
1502      #   (so we need the *union* of two sets of generators),
1503      if IsString( identifier[2] ) or Length( identifier[2] ) = 1 then
1504        # There is just one program.
1505        result:= AtlasProgramDefault( type, identifier, groupname );
1506      elif Length( identifier[2] ) = 2 then
1507        # The second entry describes two files.
1508        entry:= identifier[2][1];
1509        if IsString( entry ) then
1510          prog:= AGR.FileContents( [ [ "dataword", entry ] ], type );
1511          names:= [ entry ];
1512        else
1513          prog:= AGR.FileContents( [ entry ], type );
1514          names:= [ entry[2] ];
1515        fi;
1516        if prog = fail then
1517          return fail;
1518        fi;
1519
1520        entry:= identifier[2][2];
1521        if IsString( entry ) then
1522          Add( names, entry );
1523        else
1524          Add( names, entry[2] );
1525          entry:= [ entry ];
1526        fi;
1527
1528        # Decide in which situation we are.
1529        pos:= Position( names[2], '-' );
1530        if pos <> fail and names[2]{ [ 1 .. pos - 1 ] }
1531                           = ReplacedString( names[1], "-", "" ) then
1532          # One program for the restriction, one for the standardization.
1533          type:= First( AGR.DataTypes( "prg" ), x -> x[1] = "maxstd" );
1534          maxstd:= AtlasProgramDefault( type, [ groupname, entry, std ],
1535                       groupname );
1536          if maxstd = fail then
1537            return fail;
1538          fi;
1539          prog:= CompositionOfStraightLinePrograms( maxstd.program,
1540                                                    prog.program );
1541        else
1542          # One program for a factor group and some kernel generators
1543          # must be integrated.
1544          type:= First( AGR.DataTypes( "prg" ), x -> x[1] = "kernel" );
1545          kerprg:= AtlasProgramDefault( type, [ groupname, entry, std ],
1546                       groupname );
1547          if kerprg = fail then
1548            return fail;
1549          fi;
1550          prog:= [ prog.program, kerprg.program ];
1551          prog:= IntegratedStraightLineProgramExt( prog );
1552        fi;
1553        result:= rec( program         := prog,
1554                      standardization := std,
1555                      identifier      := identifier );
1556      else
1557        return fail;
1558      fi;
1559
1560      # Set subgroup size and subgroup name if available.
1561      gapname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
1562                       pair -> pair[2] = groupname );
1563      if IsBound( gapname[3].sizesMaxes ) and
1564         IsBound( gapname[3].sizesMaxes[i] ) then
1565        result.size:= gapname[3].sizesMaxes[i];
1566      fi;
1567      if IsBound( gapname[3].structureMaxes ) and
1568         IsBound( gapname[3].structureMaxes[i] ) then
1569        result.subgroupname:= gapname[3].structureMaxes[i];
1570      fi;
1571
1572      return result;
1573    end,
1574
1575    # entry: `[ <std>, <maxnr>, <file> ]',
1576    # conditions: `[ "maxes", <maxnr> ]' or `[ "maxes", <maxnr>, <std2> ]'
1577    #             or together with `[ "version", <vers> ]'
1578    AccessPRG := function( toc, groupname, std, conditions )
1579      local std2, version, record, entry, mxstd, info, factgroupinfo, maxext,
1580            finfo, factgroupname, i, istd, ker;
1581
1582      std2:= true;
1583      version:= true;
1584      if not ( Length( conditions ) in [ 2 .. 5 ] and conditions[1] = "maxes"
1585               and IsPosInt( conditions[2] ) ) then
1586        return fail;
1587      elif Length( conditions ) = 3 then
1588        std2:= conditions[3];
1589        if not IsPosInt( std2 ) then
1590          return fail;
1591        fi;
1592      elif Length( conditions ) = 4 then
1593        if conditions[3] <> "version" then
1594          return fail;
1595        fi;
1596        version:= String( conditions[4] );
1597      elif Length( conditions ) = 5 then
1598        std2:= conditions[3];
1599        if not ( IsPosInt( std2 ) and conditions[4] = "version" ) then
1600          return fail;
1601        fi;
1602        version:= String( conditions[5] );
1603      fi;
1604
1605      if IsBound( toc.( groupname ) ) then
1606        record:= toc.( groupname );
1607
1608        # If a straight line program for the restriction is available
1609        # then take it.
1610        if IsBound( record.maxes ) then
1611          for entry in record.maxes do
1612            if     ( std = true or entry[1] in std )
1613               and entry[2] = conditions[2] then
1614              if version = true or AGR.VersionOfSLP( entry[3] ) = version then
1615                # Note that the version number refers to the straight line
1616                # program for computing the restriction, not to the program
1617                # for standardizing the result of the restriction.
1618                # (This feature is needed by 'BrowseAtlasInfo'.)
1619                if std2 = true then
1620                  # We need not standardize the subgroup generators.
1621                  entry:= entry{ [ 3, 1 ] };
1622                  if toc.TocID <> "core" then
1623                    entry[1]:= [ [ toc.TocID, entry[1] ] ];
1624                  fi;
1625                  return entry;
1626                else
1627                  # We have to find a slp for computing *standard* generators
1628                  # of the max. subgp., perhaps in another table of contents.
1629                  mxstd:= AGR.StandardizeMaximalSubgroup( groupname,
1630                              entry[3], std2, true );
1631                  if mxstd <> fail then
1632                    entry:= [ [ entry[3], mxstd[1] ], entry[1] ];
1633                    if toc.TocID <> "core" then
1634                      entry[1][1]:= [ toc.TocID, entry[1][1] ];
1635                    fi;
1636                    return entry;
1637                  fi;
1638                fi;
1639              fi;
1640            fi;
1641          od;
1642        fi;
1643      fi;
1644
1645      # If a straight line program is available for the restriction
1646      # to the maximal subgroup of a factor group,
1647      # and if this program can be used also here
1648      # then take it.
1649      # In this case, we cannot return *standard* generators.
1650      # We do not want to support version numbers,
1651      # they would depend on two programs.
1652      if Length( conditions ) <> 2 then
1653        return fail;
1654      fi;
1655
1656      info:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
1657                    x -> x[2] = groupname );
1658      if info = fail or not IsBound( info[3].maxext )
1659                     or not IsBound( info[3].factorCompatibility ) then
1660        return fail;
1661      fi;
1662      factgroupinfo:= Filtered( info[3].factorCompatibility,
1663                                x -> ( std = true or x[1] in std )
1664                                     and x[4] = true );
1665      maxext:= Filtered( info[3].maxext,
1666                          x -> ( std = true or x[1] in std )
1667                               and x[2] = conditions[2] );
1668      for finfo in factgroupinfo do
1669        factgroupname:= finfo[2];
1670        info:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
1671                      x -> x[1] = factgroupname );
1672        factgroupname:= info[2];
1673        if IsBound( toc.( factgroupname ) ) then
1674          record:= toc.( factgroupname );
1675          if IsBound( record.maxes ) then
1676            for i in maxext do
1677              if ForAny( record.maxes, fl -> fl[3] = i[3][1] ) then
1678                entry:= i{ [ 3, 1 ] };
1679                if Length( entry[1] ) = 1 then
1680                  # No additional kernel generators are needed.
1681                  entry[1]:= entry[1][1];
1682                  if toc.TocID <> "core" then
1683                    entry[1]:= [ [ toc.TocID, entry[1] ] ];
1684                  fi;
1685                  return entry;
1686                else
1687                  # We have to specify a program for computing the kernel.
1688                  if std = true then
1689                    ker:= AtlasProgramInfo( AGR.GAPNameAtlasName( groupname ),
1690                                            "kernel", finfo[2] );
1691                  else
1692                    for istd in std do
1693                      ker:= AtlasProgramInfo( AGR.GAPNameAtlasName( groupname ),
1694                                              istd, "kernel", finfo[2] );
1695                      if ker <> fail then
1696                        break;
1697                      fi;
1698                    od;
1699                  fi;
1700                  if ker <> fail then
1701                    entry[1]:= ShallowCopy( entry[1] );
1702                    if IsString( ker.identifier[2] ) then
1703                      entry[1][2]:= ker.identifier[2];
1704                    else
1705                      entry[1][2]:= ker.identifier[2][1];
1706                    fi;
1707                    if toc.TocID <> "core" then
1708                      entry[1][1]:= [ toc.TocID, entry[1][1] ];
1709                    fi;
1710                    return entry;
1711                  fi;
1712                fi;
1713              fi;
1714            od;
1715          fi;
1716        fi;
1717      od;
1718      return fail;
1719    end,
1720
1721    # Maxes are sorted according to their natural position.
1722    SortTOCEntries := entry -> entry[2],
1723
1724    # In addition to the tests in `AGR.TestWordsSLPDefault',
1725    # compute the images in a representation if available,
1726    # and compare the group order with that stored in the
1727    # GAP Character Table Library (if available).
1728    TestWords:= function( tocid, name, file, type, verbose )
1729        local prog, prg, gens, pos, pos2, maxnr, gapname, storedsize, tbl,
1730              subname, subtbl, std, grp, size;
1731
1732        # Read the program.
1733        if tocid = "core" then
1734          tocid:= "dataword";
1735        fi;
1736        prog:= AGR.FileContents( [ [ tocid, file ] ], type );
1737        if prog = fail then
1738          Print( "#E  file `", file, "' is corrupted\n" );
1739          return false;
1740        fi;
1741
1742        # Check consistency.
1743        if prog = fail or not IsInternallyConsistent( prog.program ) then
1744          Print( "#E  program `", file, "' not internally consistent\n" );
1745          return false;
1746        fi;
1747        prg:= prog.program;
1748
1749        # Create a list of trivial generators.
1750        gens:= ListWithIdenticalEntries(
1751                   NrInputsOfStraightLineProgram( prg ), () );
1752
1753        # Run the program.
1754        gens:= ResultOfStraightLineProgram( prg, gens );
1755
1756        # Compute the position in the `Maxes' list.
1757        pos:= PositionSublist( file, "-max" );
1758        pos2:= pos + 4;
1759        while file[ pos2 ] <> 'W' do
1760          pos2:= pos2 + 1;
1761        od;
1762        maxnr:= Int( file{ [ pos+4 .. pos2-1 ] } );
1763
1764        # Fetch a perhaps stored value.
1765        gapname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
1766                         pair -> name = pair[2] );
1767        if gapname = fail then
1768          Print( "#E  problem: no GAP name for `", name, "'\n" );
1769          return false;
1770        fi;
1771        storedsize:= fail;
1772        if IsBound( gapname[3].sizesMaxes ) and
1773           IsBound( gapname[3].sizesMaxes[ maxnr ] ) then
1774          storedsize:= gapname[3].sizesMaxes[ maxnr ];
1775        fi;
1776
1777        # Identify the group in the GAP Character Table Library.
1778        tbl:= CharacterTable( gapname[1] );
1779        if tbl = fail and storedsize = fail then
1780          if verbose then
1781            Print( "#I  no character table for `", gapname[1],
1782                   "', no check for `", file, "'\n" );
1783          fi;
1784          return true;
1785        fi;
1786
1787        # Identify the subgroup in the GAP Character Table Library.
1788        if tbl <> fail then
1789          if HasMaxes( tbl ) then
1790            if Length( Maxes( tbl ) ) < maxnr then
1791              Print( "#E  program `", file,
1792                     "' contradicts `Maxes( ", tbl, " )'\n" );
1793              return false;
1794            fi;
1795            subname:= Maxes( tbl )[ maxnr ];
1796          else
1797            subname:= Concatenation( Identifier( tbl ), "M", String( maxnr ) );
1798          fi;
1799          subtbl:= CharacterTable( subname );
1800          if IsCharacterTable( subtbl ) then
1801            if storedsize <> fail and storedsize <> Size( subtbl ) then
1802              Print( "#E  program `", file,
1803                     "' contradicts stored subgroup order'\n" );
1804              return false;
1805            elif storedsize = fail then
1806              storedsize:= Size( subtbl );
1807            fi;
1808          elif storedsize = fail then
1809            if verbose then
1810              Print( "#I  no character table for `", subname,
1811                     "', no check for `", file, "'\n" );
1812            fi;
1813            return true;
1814          fi;
1815        fi;
1816        if storedsize = fail then
1817          return true;
1818        fi;
1819
1820        # Compute the standardization.
1821        pos2:= pos - 1;
1822        while file[ pos2 ] <> 'G' do
1823          pos2:= pos2-1;
1824        od;
1825        std:= Int( file{ [ pos2+1 .. pos-1 ] } );
1826
1827        # Get a representation if available, and map the generators.
1828        gapname:= gapname[1];
1829        gens:= OneAtlasGeneratingSetInfo( gapname, std,
1830                   NrMovedPoints, [ 2 .. AGR.Test.MaxTestDegree ],
1831                   "contents", [ tocid, "local" ] );
1832        if gens = fail then
1833          if verbose then
1834            Print( "#I  no perm. repres. for `", gapname,
1835                   "', no check for `", file, "'\n" );
1836          fi;
1837        else
1838          gens:= AtlasGenerators( gens );
1839          grp:= Group( gens.generators );
1840          if tbl <> fail then
1841            if IsBound( gens.size ) and gens.size <> Size( tbl ) then
1842              Print( "#E  wrong size for group`", gapname, "'\n" );
1843              return false;
1844            fi;
1845            SetSize( grp, Size( tbl ) );
1846          fi;
1847          gens:= ResultOfStraightLineProgram( prg, gens.generators );
1848          size:= Size( SubgroupNC( grp, gens ) );
1849          if size <> storedsize then
1850            Print( "#E  program `", file, "' for group of order ", size,
1851                   " not ", storedsize, "\n" );
1852            if subtbl <> fail then
1853              Print( "#E  (contradicts character table of `",
1854                     Identifier( subtbl ), "')\n" );
1855            fi;
1856            return false;
1857          fi;
1858        fi;
1859
1860        # No more tests are available.
1861        return true;
1862    end,
1863
1864    # There is only one file.
1865    ReadAndInterpretDefault := paths -> ScanStraightLineProgram( paths[1] ),
1866    ) );
1867
1868
1869#############################################################################
1870##
1871#D  Straight line programs for class representatives
1872##
1873##  <#GAPDoc Label="type:classes:format">
1874##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-cclsW</C><M>n</M></Mark>
1875##  <Item>
1876##    In this case, the file contains a straight line program that returns
1877##    a list of conjugacy class representatives of <M>G</M>.
1878##    An example is <C>RuG1-cclsW1</C>.
1879##  </Item>
1880##  <#/GAPDoc>
1881##
1882AGR.DeclareDataType( "prg", "classes", rec(
1883
1884    # `<groupname>G<i>-cclsW<n>'
1885    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
1886                          [ "cclsW", IsDigitChar ] ],
1887                        [ ParseBackwards, ParseForwards ] ],
1888
1889    # `[ <i>, <filename> ]'
1890    AddFileInfo := function( list, entry, name )
1891      Add( list, Concatenation( entry{ [ 3 ] }, [ name ] ) );
1892      return true;
1893    end,
1894
1895    DisplayOverviewInfo := [ "cl", "c", function( conditions )
1896      local groupname, tocs, std, value, private, toc, record, i, pos, rel;
1897
1898      groupname:= conditions[1][2];
1899      tocs:= AGR.TablesOfContents( conditions );
1900      if Length( conditions ) = 1 or
1901         not ( IsInt( conditions[2] ) or IsList( conditions[2] ) ) then
1902        std:= true;
1903      else
1904        std:= conditions[2];
1905        if IsInt( std ) then
1906          std:= [ std ];
1907        fi;
1908      fi;
1909
1910      value:= false;
1911      private:= false;
1912      for toc in tocs do
1913        if IsBound( toc.( groupname ) ) then
1914          record:= toc.( groupname );
1915          if IsBound( record.classes ) and
1916             ( ( std = true and not IsEmpty( record.classes ) ) or
1917               ForAny( record.classes, l -> l[1] in std ) ) then
1918            value:= true;
1919          elif IsBound( record.cyc2ccl ) and IsBound( record.cyclic ) then
1920            for i in record.cyc2ccl do
1921              # Check that for scripts of the form
1922              # `<groupname>G<i>cycW<n>-cclsW<m>',
1923              # a script of the form `<groupname>G<i>-cycW<n>' is available.
1924              pos:= PositionSublist( i[2], "cycW" );
1925              rel:= Concatenation( i[2]{ [ 1 .. pos-1 ] }, "-",
1926                        i[2]{ [ pos .. Position( i[2], '-' ) - 1 ] } );
1927              if ( std = true or i[1] in std ) and
1928                 ForAny( record.cyclic,
1929                     x -> x[2] = rel and ( std = true or x[1] in std ) ) then
1930                value:= true;
1931                break;
1932              fi;
1933            od;
1934          fi;
1935          if value then
1936            if toc.TocID <> "core" then
1937              private:= true;
1938            fi;
1939            break;
1940          fi;
1941        fi;
1942      od;
1943      if value then
1944        value:= "+";
1945      else
1946        value:= "";
1947      fi;
1948      return [ value, private ];
1949    end ],
1950
1951    DisplayPRG := function( tocs, names, std, stdavail )
1952      local data, c2c, cyc, toc, record, private, i, filec2c, filecyc, pos,
1953            rel, match, entry;
1954
1955      data:= [];
1956
1957      # The information can be stored either directly or via two scripts
1958      # in `cyclic' and `cyc2ccl'.
1959      c2c:= [];
1960      cyc:= [];
1961
1962      for toc in tocs do
1963        if IsBound( toc.( names[2] ) ) then
1964          record:= toc.( names[2] );
1965          if toc.TocID <> "core" then
1966            private:= UserPreference( "AtlasRep",
1967                          "AtlasRepMarkNonCoreData" );
1968          else
1969            private:= "";
1970          fi;
1971          if IsBound( record.classes ) then
1972            for i in record.classes do
1973              if std = true or i[1] in std then
1974                entry:= [ "",
1975                          private,
1976                          String( i[1] ),
1977                          AGR.VersionOfSLP( i[2] ),
1978                          [ names[1], i[2], i[1] ] ];
1979                if toc.TocID <> "core" then
1980                  entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
1981                fi;
1982                Add( data, entry );
1983              fi;
1984            od;
1985          fi;
1986
1987          if IsBound( record.cyc2ccl ) then
1988            for i in record.cyc2ccl do
1989              if std = true or i[1] in std then
1990                entry:= [ i, private ];
1991                if toc.TocID <> "core" then
1992                  entry[3]:= toc.TocID;
1993                fi;
1994                Add( c2c, entry );
1995              fi;
1996            od;
1997          fi;
1998
1999          if IsBound( record.cyclic ) then
2000            for i in record.cyclic do
2001              if std = true or i[1] in std then
2002                entry:= [ i, private ];
2003                if toc.TocID <> "core" then
2004                  entry[3]:= toc.TocID;
2005                fi;
2006                Add( cyc, entry );
2007              fi;
2008            od;
2009          fi;
2010        fi;
2011      od;
2012
2013      for i in c2c do
2014
2015        # Check if for scripts of the form `<groupname>G<i>cycW<n>-cclsW<m>',
2016        # a script of the form `<groupname>G<i>-cycW<n>' is available.
2017        filec2c:= i[1][2];
2018        pos:= PositionSublist( filec2c, "cycW" );
2019        rel:= Concatenation( filec2c{ [ 1 .. pos-1 ] }, "-",
2020                  filec2c{ [ pos .. Position( filec2c, '-' ) - 1 ] } );
2021        match:= First( cyc, x -> x[1][2] = rel );
2022        if match <> fail then
2023          private:= "";
2024          if i[2] <> "" then
2025            private:= i[2];
2026          elif match[2] <> "" then
2027            private:= match[2];
2028          fi;
2029          if Length( i ) = 3 then
2030            filec2c:= [ i[3], filec2c ];
2031          fi;
2032          filecyc:= match[1][2];
2033          if Length( match ) = 3 then
2034            filecyc:= [ match[3], filecyc ];
2035          fi;
2036          entry:= [ "(composed)",
2037                    private,
2038                    String( match[1][1] ),
2039                    JoinStringsWithSeparator( AGR.VersionOfSLP( filec2c ),
2040                                              ", " ),
2041                    [ names[1], [ filec2c, filecyc ], match[1][1] ] ];
2042          Add( data, entry );
2043        fi;
2044      od;
2045
2046      if ForAny( data, x -> x[1] = "(composed)" ) then
2047        for i in data do
2048          if i[1] = "" then
2049            i[1]:= "(direct)";
2050          fi;
2051        od;
2052      fi;
2053
2054      return AGR.CommonDisplayPRG( "class repres.", stdavail, data, true );
2055    end,
2056
2057    # entry: `[ <std>, <file> ]',
2058    # conditions: `[ "classes" ]'
2059    #             or together with `[ "version", <vers> ]'
2060    AccessPRG := function( toc, groupname, std, conditions )
2061      local version, record, entry, toc2, record2, pos, rel, entry2, file2;
2062
2063      if not IsBound( toc.( groupname ) ) then
2064        return fail;
2065      elif Length( conditions ) = 1 and conditions[1] = "classes" then
2066        version:= true;
2067      elif Length( conditions ) = 3 and conditions[1] = "classes"
2068                                    and conditions[2] = "version" then
2069        version:= String( conditions[3] );
2070      else
2071        return fail;
2072      fi;
2073
2074      # Check whether there is a program for computing class repres.
2075      record:= toc.( groupname );
2076      if IsBound( record.classes ) then
2077        for entry in record.classes do
2078          if ( std = true or entry[1] in std ) and
2079             ( version = true or AGR.VersionOfSLP( entry[2] ) = version ) then
2080            entry:= entry{ [ 2, 1 ] };
2081            if toc.TocID <> "core" then
2082              entry[1]:= [ [ toc.TocID, entry[1] ] ];
2083            fi;
2084            return entry;
2085          fi;
2086        od;
2087      fi;
2088
2089      # Try to compose the program for computing classes
2090      # from a program for computing repres. of cyclic subgroups
2091      # (in the given table of contents)
2092      # and a program for computing class representatives from the outputs of
2093      # this program (in *any* table of contents).
2094      for toc2 in AGR.TablesOfContents( "all" ) do
2095        if IsBound( toc2.( groupname ) ) then
2096          record2:= toc2.( groupname );
2097
2098          if IsBound( record.cyclic ) and IsBound( record2.cyc2ccl )
2099                                      and version = true then
2100            for entry2 in record2.cyc2ccl do
2101              if std = true or entry2[1] in std then
2102
2103                # Check if for `<groupname>G<i>cycW<n>-cclsW<m>' scripts,
2104                # a script of the form `<groupname>G<i>-cycW<n>' exists.
2105                file2:= entry2[2];
2106                pos:= PositionSublist( file2, "cycW" );
2107                rel:= Concatenation( file2{ [ 1 .. pos-1 ] }, "-",
2108                        file2{ [ pos .. Position( file2, '-' ) - 1 ] } );
2109                for entry in record.cyclic do
2110                  if entry[2] = rel and ( std = true or entry[1] in std ) then
2111                    if toc.TocID <> "core" then
2112                      rel:= [ toc.TocID, rel ];
2113                    fi;
2114                    if toc.TocID <> "core" then
2115                      file2:= [ toc2.TocID, file2 ];
2116                    fi;
2117                    return [ [ file2, rel ], entry2[1] ];
2118                  fi;
2119                od;
2120              fi;
2121            od;
2122          fi;
2123        fi;
2124      od;
2125
2126      return fail;
2127    end,
2128
2129    # Create the program info from the identifier.
2130    AtlasProgramInfo := function( type, identifier, groupname )
2131      local filename;
2132
2133      # If only one file is involved then use the default function.
2134      filename:= identifier[2];
2135      if IsString( filename ) or Length( filename ) = 1 then
2136        return AtlasProgramInfoDefault( type, identifier, groupname );
2137      fi;
2138
2139      # Two files are involved.
2140      filename:= identifier[2][1];
2141      if not IsString( filename ) then
2142        filename:= filename[2];
2143      fi;
2144      type:= First( AGR.DataTypes( "prg" ), x -> x[1] = "cyc2ccl" );
2145      if AGR.ParseFilenameFormat( filename, type[2].FilenameFormat )
2146             = fail then
2147        return fail;
2148      fi;
2149
2150      filename:= identifier[2][2];
2151      if not IsString( filename ) then
2152        filename:= filename[2];
2153      fi;
2154      type:= First( AGR.DataTypes( "prg" ), x -> x[1] = "cyclic" );
2155      if AGR.ParseFilenameFormat( filename, type[2].FilenameFormat )
2156             = fail then
2157        return fail;
2158      fi;
2159
2160      return rec( standardization := identifier[3],
2161                  identifier      := identifier );
2162    end,
2163
2164    # Create the program from the identifier.
2165    AtlasProgram := function( type, identifier, groupname )
2166      local type1, entry1, filename, type2, entry2, prog1, prog2, prog,
2167            result;
2168
2169      if IsString( identifier[2] ) or Length( identifier[2] ) = 1 then
2170        # The second entry describes one file.
2171        return AtlasProgramDefault( type, identifier, groupname );
2172      elif Length( identifier[2] ) = 2 then
2173        # The second entry describes two files to be composed.
2174        type1:= First( AGR.DataTypes( "prg" ), x -> x[1] = "cyclic" );
2175        entry1:= identifier[2][2];
2176        if IsString( entry1 ) then
2177          filename:= entry1;
2178          entry1:= [ "dataword", entry1 ];
2179        else
2180          filename:= entry1[2];
2181        fi;
2182        if AGR.ParseFilenameFormat( filename, type1[2].FilenameFormat )
2183               = fail then
2184          return fail;
2185        fi;
2186
2187        type2:= First( AGR.DataTypes( "prg" ), x -> x[1] = "cyc2ccl" );
2188        entry2:= identifier[2][1];
2189        if IsString( entry2 ) then
2190          filename:= entry2;
2191          entry2:= [ "dataword", entry2 ];
2192        else
2193          filename:= entry2[2];
2194        fi;
2195        if AGR.ParseFilenameFormat( filename, type2[2].FilenameFormat )
2196               = fail then
2197          return fail;
2198        fi;
2199
2200        prog1:= AGR.FileContents( [ entry1 ], type1 );
2201        if prog1 = fail then
2202          return fail;
2203        fi;
2204        prog2:= AGR.FileContents( [ entry2 ], type2 );
2205        if prog2 = fail then
2206          return fail;
2207        fi;
2208
2209        prog:= CompositionOfStraightLinePrograms( prog2.program,
2210                   prog1.program );
2211        if prog = fail then
2212          return fail;
2213        fi;
2214
2215        result:= rec( program         := prog,
2216                      standardization := identifier[3],
2217                      identifier      := identifier );
2218
2219        if IsBound( prog2.outputs ) then
2220          # Take the outputs of the last program in the composition.
2221          result.outputs:= prog2.outputs;
2222        fi;
2223
2224        return result;
2225      fi;
2226
2227      return fail;
2228    end,
2229
2230    TestWords := function( tocid, name, file, type, verbose )
2231      return AGR.TestWordsSLPDefault( tocid, name, file, type, true, verbose );
2232    end,
2233
2234    # There is only one file.
2235    ReadAndInterpretDefault := paths -> ScanStraightLineProgram( paths[1] ),
2236    ) );
2237
2238
2239#############################################################################
2240##
2241#D  Straight line programs for representatives of cyclic subgroups
2242##
2243##  <#GAPDoc Label="type:cyclic:format">
2244##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-cycW</C><M>n</M></Mark>
2245##  <Item>
2246##    In this case, the file contains a straight line program that returns
2247##    a list of representatives of generators
2248##    of maximally cyclic subgroups of <M>G</M>.
2249##    An example is <C>Co1G1-cycW1</C>.
2250##  </Item>
2251##  <#/GAPDoc>
2252##
2253AGR.DeclareDataType( "prg", "cyclic", rec(
2254    # `<groupname>G<i>-cycW<n>'
2255    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
2256                          [ "cycW", IsDigitChar ] ],
2257                        [ ParseBackwards, ParseForwards ] ],
2258
2259    # `[ <i>, <filename> ]'
2260    AddFileInfo := function( list, entry, name )
2261      Add( list, Concatenation( entry{ [ 3 ] }, [ name ] ) );
2262      return true;
2263    end,
2264
2265    DisplayOverviewInfo := AGR.DisplayOverviewInfoDefault( "cyc", "c", "cyclic" ),
2266
2267    DisplayPRG := function( tocs, names, std, stdavail )
2268      local data, toc, record, private, i, entry;
2269
2270      data:= [];
2271
2272      for toc in tocs do
2273        if IsBound( toc.( names[2] ) ) then
2274          record:= toc.( names[2] );
2275          if IsBound( record.cyclic ) then
2276            if toc.TocID <> "core" then
2277              private:= UserPreference( "AtlasRep",
2278                            "AtlasRepMarkNonCoreData" );
2279            else
2280              private := "";
2281            fi;
2282            for i in record.cyclic do
2283              if std = true or i[1] in std then
2284                entry:= [ "",
2285                          private,
2286                          String( i[1] ),
2287                          AGR.VersionOfSLP( i[2] ),
2288                          [ names[1], i[2], i[1] ] ];
2289                if toc.TocID <> "core" then
2290                  entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
2291                fi;
2292                Add( data, entry );
2293              fi;
2294            od;
2295          fi;
2296        fi;
2297      od;
2298
2299      return AGR.CommonDisplayPRG( "repr. cyc. subg.", stdavail, data, true );
2300    end,
2301
2302    # entry: `[ <std>, <file> ]',
2303    # conditions: `[ "cyclic" ]'
2304    #             or together with `[ "version", <vers> ]'
2305    AccessPRG := function( toc, groupname, std, conditions )
2306      local version, record, entry;
2307
2308      if not IsBound( toc.( groupname ) ) then
2309        return fail;
2310      elif Length( conditions ) = 1 and conditions[1] = "cyclic" then
2311        version:= true;
2312      elif Length( conditions ) = 3 and conditions[1] = "cyclic"
2313                                    and conditions[2] = "version" then
2314        version:= String( conditions[3] );
2315      else
2316        return fail;
2317      fi;
2318
2319      record:= toc.( groupname );
2320      if IsBound( record.cyclic ) then
2321        for entry in record.cyclic do
2322          if ( std = true or entry[1] in std ) and
2323             ( version = true or AGR.VersionOfSLP( entry[2] ) = version ) then
2324            entry:= entry{ [ 2, 1 ] };
2325            if toc.TocID <> "core" then
2326              entry[1]:= [ [ toc.TocID, entry[1] ] ];
2327            fi;
2328            return entry;
2329          fi;
2330        od;
2331      fi;
2332      return fail;
2333    end,
2334
2335    TestWords := function( tocid, name, file, type, verbose )
2336      return AGR.TestWordsSLPDefault( tocid, name, file, type, true, verbose );
2337    end,
2338
2339    # There is only one file.
2340    ReadAndInterpretDefault := paths -> ScanStraightLineProgram( paths[1] ),
2341    ) );
2342
2343
2344#############################################################################
2345##
2346#D  Straight line programs for computing class representatives from
2347#D      representatives of cyclic subgroups
2348##
2349##  <#GAPDoc Label="type:cyc2ccls:format">
2350##  <Mark><M>groupname</M><C>G</C><M>i</M><C>cycW</C><M>n</M><C>-cclsW</C><M>m</M></Mark>
2351##  <Item>
2352##    In this case, the file contains a straight line program that takes
2353##    the return value of the program in the file
2354##    <M>groupname</M><C>G</C><M>i</M><C>-cycW</C><M>n</M>
2355##    (see above),
2356##    and returns a list of conjugacy class representatives of <M>G</M>.
2357##    An example is <C>M11G1cycW1-cclsW1</C>.
2358##  </Item>
2359##  <#/GAPDoc>
2360##
2361AGR.DeclareDataType( "prg", "cyc2ccl", rec(
2362
2363    # `<groupname>G<i>cycW<n>-cclsW<m>'
2364    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar, "cycW", IsDigitChar ],
2365                          [ "cclsW", IsDigitChar ] ],
2366                        [ ParseBackwards, ParseForwards ] ],
2367
2368    # `[ <i>, <filename> ]'
2369    AddFileInfo := function( list, entry, name )
2370      Add( list, Concatenation( entry{ [ 3 ] }, [ name ] ) );
2371      return true;
2372    end,
2373
2374    # entry: `[ <std>, <file> ]',
2375    # conditions: `[ "cyc2ccl" ]' or
2376    #             `[ "cyc2ccl", <vers> ]' or
2377    #             `[ "cyc2ccl", "version", <version> ]' or
2378    #             `[ "cyc2ccl", <vers>, "version", <version> ]'
2379    #             where <vers> is the version number of the 'cyc' script
2380    #             and <version> is the version number of the program itself
2381    AccessPRG := function( toc, groupname, std, conditions )
2382      local version, record, versions, entry;
2383
2384      if not IsBound( toc.( groupname ) ) then
2385        return fail;
2386      elif Length( conditions ) = 1 and conditions[1] = "cyc2ccl" then
2387        version:= true;
2388      elif Length( conditions ) = 2 and conditions[1] = "cyc2ccl" then
2389        version:= [ conditions[2], true ];
2390      elif Length( conditions ) = 3 and conditions[1] = "cyc2ccl"
2391                                    and conditions[2] = "version" then
2392        version:= [ true, conditions[3] ];
2393      elif Length( conditions ) = 4 and conditions[1] = "cyc2ccl"
2394                                    and conditions[3] = "version" then
2395        version:= [ conditions[2], conditions[4] ];
2396      else
2397        return fail;
2398      fi;
2399
2400      record:= toc.( groupname );
2401      if IsBound( record.cyc2ccl ) then
2402        for entry in record.cyc2ccl do
2403          if version <> true then
2404            # Note that 'AGR.VersionOfSLP' returns two strings in this case.
2405            versions:= AGR.VersionOfSLP( entry[2] );
2406          fi;
2407          if ( std = true or entry[1] in std ) and
2408             ( version = true or
2409               ( ( version[1] = true or
2410                   String( version[1] ) = versions[1] ) and
2411                 ( version[2] = true or
2412                   String( version[2] ) = versions[2] ) ) ) then
2413            entry:= entry{ [ 2, 1 ] };
2414            if toc.TocID <> "core" then
2415              entry[1]:= [ [ toc.TocID, entry[1] ] ];
2416            fi;
2417            return entry;
2418          fi;
2419        od;
2420      fi;
2421      return fail;
2422    end,
2423
2424    TestWords := function( tocid, name, file, type, verbose )
2425      return AGR.TestWordsSLPDefault( tocid, name, file, type, true, verbose );
2426    end,
2427
2428    # There is only one file.
2429    ReadAndInterpretDefault := paths -> ScanStraightLineProgram( paths[1] ),
2430    ) );
2431
2432
2433#############################################################################
2434##
2435#D  Straight line programs for computing kernel generators
2436##
2437##  <#GAPDoc Label="type:kernel:format">
2438##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-ker</C><M>factgroupname</M><C>W</C><M>n</M></Mark>
2439##  <Item>
2440##    In this case, the file contains a straight line program that takes
2441##    generators of <M>G</M> w.&nbsp;r.&nbsp;t.&nbsp;the <M>i</M>-th set of
2442##    standard generators,
2443##    and returns generators of the kernel of an epimorphism
2444##    that maps <M>G</M> to a group with <Package>ATLAS</Package>-file name
2445##    <M>factgroupname</M>.
2446##    An example is <C>2A5G1-kerA5W1</C>.
2447##  </Item>
2448##  <#/GAPDoc>
2449##
2450AGR.DeclareDataType( "prg", "kernel", rec(
2451
2452    # `<groupname>G<i>-ker<factgroupname>W<n>'
2453    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
2454                          [  "ker", IsChar, "W", IsDigitChar ] ],
2455                        [ ParseBackwards, ParseBackwardsWithPrefix ] ],
2456
2457    # `[ <i>, <factgroupname>, <filename> ]'
2458    AddFileInfo := function( list, entry, name )
2459      Add( list, [ entry[3], entry[5], name ] );
2460      return true;
2461    end,
2462
2463    # no DisplayOverviewInfo function
2464    DisplayPRG := function( tocs, names, std, stdavail )
2465      local data, gapname, toc, record, private, i, entry;
2466
2467      data:= [];
2468      if AGR.ShowOnlyASCII() then
2469        gapname:= Concatenation( names[1], " -> " );
2470      else
2471        gapname:= Concatenation( names[1], " → " );
2472      fi;
2473
2474      for toc in tocs do
2475        if IsBound( toc.( names[2] ) ) then
2476          record:= toc.( names[2] );
2477          if IsBound( record.kernel ) then
2478            if toc.TocID <> "core" then
2479              private:= UserPreference( "AtlasRep",
2480                            "AtlasRepMarkNonCoreData" );
2481            else
2482              private := "";
2483            fi;
2484            for i in record.kernel do
2485              if std = true or i[1] in std then
2486                entry:= [ Concatenation( gapname,
2487                                         AGR.GAPNameAtlasName( i[2] ) ),
2488                          private,
2489                          String( i[1] ),
2490                          AGR.VersionOfSLP( i[3] ),
2491                          [ names[1], i[3], i[1] ] ];
2492                if toc.TocID <> "core" then
2493                  entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
2494                fi;
2495                Add( data, entry );
2496              fi;
2497            od;
2498          fi;
2499        fi;
2500      od;
2501
2502      return AGR.CommonDisplayPRG( "kernels", stdavail, data, false );
2503    end,
2504
2505    # entry: `[ <std>, <descr>, <file> ]',
2506    # conditions: `[ "kernel", <factgroupname> ]'
2507    #             or together with `[ "version", <vers> ]'
2508    AccessPRG := function( toc, groupname, std, conditions )
2509      local version, record, info, entry;
2510
2511      if not IsBound( toc.( groupname ) ) then
2512        return fail;
2513      elif Length( conditions ) = 2 and conditions[1] = "kernel" then
2514        version:= true;
2515      elif Length( conditions ) = 4 and conditions[1] = "kernel"
2516                                    and conditions[3] = "version" then
2517        version:= String( conditions[4] );
2518      else
2519        return fail;
2520      fi;
2521
2522      record:= toc.( groupname );
2523      if IsBound( record.kernel ) then
2524        info:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
2525                      x -> x[1] = conditions[2] );
2526        if info = fail then
2527          return fail;
2528        fi;
2529        info:= info[2];
2530
2531        for entry in record.kernel do
2532          if ( std = true or entry[1] in std ) and
2533             ( version = true or AGR.VersionOfSLP( entry[3] ) = version ) and
2534             info = entry[2] then
2535            entry:= entry{ [ 3, 1, 2 ] };
2536            if toc.TocID <> "core" then
2537              entry[1]:= [ [ toc.TocID, entry[1] ] ];
2538            fi;
2539            return entry;
2540          fi;
2541        od;
2542      fi;
2543      return fail;
2544    end,
2545
2546    TestWords := function( tocid, name, file, type, verbose )
2547      return AGR.TestWordsSLPDefault( tocid, name, file, type, false, verbose );
2548    end,
2549
2550    # There is only one file.
2551    ReadAndInterpretDefault := paths -> ScanStraightLineProgram( paths[1] ),
2552    ) );
2553
2554
2555#############################################################################
2556##
2557#D  Straight line programs for standardizing generators of maximal subgroups
2558##
2559##  <#GAPDoc Label="type:maxstd:format">
2560##  <Mark><M>groupname</M><C>G</C><M>i</M><C>max</C><M>k</M><C>W</C><M>n</M><C>-</C><M>subgroupname</M><C>G</C><M>j</M><C>W</C><M>m</M></Mark>
2561##  <Item>
2562##    In this case, the file contains a straight line program that takes
2563##    the return value of the program in the file
2564##    <M>groupname</M><C>G</C><M>i</M><C>-max</C><M>k</M><C>W</C><M>n</M>
2565##    (see above),
2566##    which are generators for a group <M>U</M>, say;
2567##    <M>subgroupname</M> is a name for <M>U</M>,
2568##    and the return value is a list of standard generators for <M>U</M>,
2569##    w.&nbsp;r.&nbsp;t.&nbsp;the <M>j</M>-th set of standard generators.
2570##    (Of course this implies that the groups in the <M>k</M>-th class of
2571##    maximal subgroups of <M>G</M> are isomorphic to the group with name
2572##    <M>subgroupname</M>.)
2573##    An example is <C>J1G1max1W1-L211G1W1</C>;
2574##    the first class of maximal subgroups of the Janko group <M>J_1</M>
2575##    consists of groups isomorphic to the linear group <M>L_2(11)</M>,
2576##    for which standard generators are defined.
2577##  </Item>
2578##  <#/GAPDoc>
2579##
2580AGR.DeclareDataType( "prg", "maxstd", rec(
2581    # `<groupname>G<i>max<k>W<n>-<subgroupname>G<j>W<m>'
2582    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar, "max", IsDigitChar,
2583                            "W", IsDigitChar ],
2584                          [ IsChar, "G", IsDigitChar, "W", IsDigitChar ] ],
2585                        [ ParseBackwards, ParseBackwards ] ],
2586
2587    # `[ <i>, <k>, <n>, <subgroupname>, <j>, <filename> ]'
2588    AddFileInfo := function( list, entry, name )
2589      Add( list, Concatenation( entry{ [ 3, 5, 7, 8, 10 ] }, [ name ] ) );
2590      return true;
2591    end,
2592
2593    # no DisplayOverviewInfo function
2594    DisplayPRG := function( tocs, names, std, stdavail )
2595      local data, toc, record, private, i, entry;
2596
2597      data:= [];
2598
2599      for toc in tocs do
2600        if IsBound( toc.( names[2] ) ) then
2601          record:= toc.( names[2] );
2602          if IsBound( record.maxstd ) then
2603            if toc.TocID <> "core" then
2604              private:= UserPreference( "AtlasRep",
2605                            "AtlasRepMarkNonCoreData" );
2606            else
2607              private := "";
2608            fi;
2609            for i in record.maxstd do
2610              if std = true or i[1] in std then
2611                entry:= [ Concatenation( "from ", Ordinal( i[2] ),
2612                                         " max., version ", String( i[3] ),
2613                                         " to ",
2614                                         AGR.GAPNameAtlasName( i[4] ),
2615                                         ", std. ", String( i[5] ) ),
2616                          private,
2617                          String( i[1] ),
2618                          AGR.VersionOfSLP( i[6] ),
2619                          [ names[1], i[6], i[1] ] ];
2620                if toc.TocID <> "core" then
2621                  entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
2622                fi;
2623                Add( data, entry );
2624              fi;
2625            od;
2626          fi;
2627        fi;
2628      od;
2629
2630      return AGR.CommonDisplayPRG( "standardizations of maxes",
2631                                   stdavail, data, false );
2632    end,
2633
2634    # Check whether ATLAS names are defined.
2635    PostprocessFileInfo := function( toc, record )
2636      local list, i;
2637      list:= record.maxstd;
2638      for i in [ 1 .. Length( list ) ] do
2639        if ForAll( AtlasOfGroupRepresentationsInfo.GAPnames,
2640                   pair -> pair[2] <> list[i][4] ) then
2641          Info( InfoAtlasRep, 3,
2642                "t.o.c. construction: ignoring name `", list[i][6], "'" );
2643          Unbind( list[i] );
2644        fi;
2645      od;
2646      if not IsDenseList( list ) then
2647        record.maxstd:= Compacted( list );
2648      fi;
2649    end,
2650
2651    # entry: `[ <std>, <maxnr>, <vers>, <subgroupname>, <substd>, <file> ]',
2652    # conditions: `[ "maxstd", <maxnr>, <vers>, <substd> ]'
2653    #             or together with `[ "version", <vers> ]'
2654    AccessPRG := function( toc, groupname, std, conditions )
2655      local record, version, entry;
2656
2657      if not IsBound( toc.( groupname ) ) then
2658        return fail;
2659      fi;
2660      record:= toc.( groupname );
2661
2662      if Length( conditions ) in [ 4, 6 ] and conditions[1] = "maxstd"
2663                                          and IsBound( record.maxstd ) then
2664        version:= true;
2665        if Length( conditions ) = 6 then
2666          if conditions[5] <> "version" then
2667            return fail;
2668          fi;
2669          version:= String( conditions[6] );
2670        fi;
2671        for entry in record.maxstd do
2672          if     ( std = true or entry[1] in std )
2673             and conditions[2] = entry[2]
2674             and conditions[3] = entry[3]
2675             and conditions[4] = entry[5]
2676             and ( version = true or
2677                   version = Int( AGR.VersionOfSLP( entry[6] ) ) ) then
2678            entry:= entry{ [ 6, 1, 2, 3, 5 ] };
2679            if toc.TocID <> "core" then
2680              entry[1]:= [ [ toc.TocID, entry[1] ] ];
2681            fi;
2682            return entry;
2683          fi;
2684        od;
2685      fi;
2686      return fail;
2687    end,
2688
2689    TestWords := function( tocid, name, file, type, verbose )
2690      return AGR.TestWordsSLPDefault( tocid, name, file, type, false, verbose );
2691    end,
2692
2693    # There is only one file.
2694    ReadAndInterpretDefault := paths -> ScanStraightLineProgram( paths[1] ),
2695    ) );
2696
2697
2698#############################################################################
2699##
2700#D  Straight line programs for computing images of standard generators
2701#D      under outer automorphisms
2702##
2703##  <#GAPDoc Label="type:out:format">
2704##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-a</C><M>outname</M><C>W</C><M>n</M></Mark>
2705##  <Item>
2706##    In this case, the file contains a straight line program that takes
2707##    generators of <M>G</M> w.&nbsp;r.&nbsp;t.&nbsp;the <M>i</M>-th set
2708##    of standard generators,
2709##    and returns the list of their images
2710##    under the outer automorphism <M>\alpha</M> of <M>G</M>
2711##    given by the name <M>outname</M>;
2712##    if this name is empty then <M>\alpha</M> is the unique nontrivial
2713##    outer automorphism of <M>G</M>;
2714##    if it is a positive integer <M>k</M> then <M>\alpha</M> is a
2715##    generator of the unique cyclic order <M>k</M> subgroup of the outer
2716##    automorphism group of <M>G</M>;
2717##    if it is of the form <C>2_1</C> or <C>2a</C>,
2718##    <C>4_2</C> or <C>4b</C>, <C>3_3</C> or <C>3c</C>
2719##    <M>\ldots</M> then <M>\alpha</M>
2720##    generates the cyclic group of automorphisms induced on <M>G</M> by
2721##    <M>G.2_1</M>, <M>G.4_2</M>, <M>G.3_3</M> <M>\ldots</M>;
2722##    finally, if it is of the form <M>k</M><C>p</C><M>d</M>,
2723##    with <M>k</M> one of the above forms and <M>d</M> an integer then
2724##    <M>d</M> denotes the number of dashes
2725##    appended to the automorphism described by <M>k</M>;
2726##    if <M>d = 1</M> then <M>d</M> can be omitted.
2727##    Examples are <C>A5G1-aW1</C>, <C>L34G1-a2_1W1</C>,
2728##    <C>U43G1-a2_3pW1</C>, and <C>O8p3G1-a2_2p5W1</C>;
2729##    these file names describe the outer order <M>2</M> automorphism of
2730##    <M>A_5</M> (induced by the action of <M>S_5</M>)
2731##    and the order <M>2</M> automorphisms of
2732##    <M>L_3(4)</M>, <M>U_4(3)</M>, and <M>O_8^+(3)</M>
2733##    induced by the actions of
2734##    <M>L_3(4).2_1</M>, <M>U_4(3).2_2^{\prime}</M>,
2735##    and <M>O_8^+(3).2_2^{{\prime\prime\prime\prime\prime}}</M>,
2736##    respectively.
2737##  </Item>
2738##  <#/GAPDoc>
2739##
2740AGR.DeclareDataType( "prg", "out", rec(
2741    # `<groupname>G<i>-a<outname>W<n>'
2742    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
2743                          [  "a", IsChar, "W", IsDigitChar ] ],
2744                        [ ParseBackwards, ParseBackwardsWithPrefix ] ],
2745
2746    # `[ <i>, <nam>, <filename> ]'
2747    AddFileInfo := function( list, entry, name )
2748      local std, descr, pos, dashes, order, index;
2749      std:= entry[3];
2750      descr:= entry[5];
2751      pos:= Position( descr, 'p' );
2752      if pos = fail then
2753        dashes:= "";
2754        pos:= Length( descr ) + 1;
2755      elif pos = Length( descr ) then
2756        dashes:= "'";
2757      else
2758        dashes:= Int( descr{ [ pos+1 .. Length( descr ) ] } );
2759        if dashes = fail then
2760          return false;
2761        fi;
2762        dashes:= ListWithIdenticalEntries( dashes, '\'' );
2763      fi;
2764      descr:= descr{ [ 1 .. pos-1 ] };
2765      pos:= Position( descr, '_' );
2766      if pos = fail then
2767        order:= descr;
2768        index:= "";
2769      else
2770        order:= descr{ [ 1 .. pos-1 ] };
2771        index:= descr{ [ pos+1 .. Length( descr ) ] };
2772      fi;
2773      if Int( order ) = fail or Int( index ) = fail then
2774        return false;
2775      elif order = "" then
2776        order:= "2";
2777      fi;
2778      if index <> "" then
2779        order:= Concatenation( order, "_", index );
2780      fi;
2781      order:= Concatenation( order, dashes );
2782      Add( list, [ std, order, name ] );
2783      return true;
2784    end,
2785
2786    DisplayOverviewInfo := [ "out", "r", function( conditions )
2787      local groupname, tocs, std, value, private, toc, record, new;
2788
2789      groupname:= conditions[1][2];
2790      tocs:= AGR.TablesOfContents( conditions );
2791      if Length( conditions ) = 1 or
2792         not ( IsInt( conditions[2] ) or IsList( conditions[2] ) ) then
2793        std:= true;
2794      else
2795        std:= conditions[2];
2796        if IsInt( std ) then
2797          std:= [ std ];
2798        fi;
2799      fi;
2800
2801      value:= [];;
2802      private:= false;
2803      for toc in tocs do
2804        if IsBound( toc.( groupname ) ) then
2805          record:= toc.( groupname );
2806          if IsBound( record.out ) then
2807            new:= Set( List( Filtered( record.out,
2808                                       x -> std = true or x[1] in std ),
2809                             x -> x[2] ) );
2810            if toc.TocID <> "core" and not IsEmpty( new ) then
2811              private:= true;
2812            fi;
2813            UniteSet( value, new );
2814          fi;
2815        fi;
2816      od;
2817      value:= JoinStringsWithSeparator( value, "," );
2818      return [ value, private ];
2819    end ],
2820
2821    DisplayPRG := function( tocs, names, std, stdavail )
2822      local data, toc, record, private, i, entry;
2823
2824      data:= [];
2825
2826      for toc in tocs do
2827        if IsBound( toc.( names[2] ) ) then
2828          record:= toc.( names[2] );
2829          if IsBound( record.out ) then
2830            if toc.TocID <> "core" then
2831              private:= UserPreference( "AtlasRep",
2832                            "AtlasRepMarkNonCoreData" );
2833            else
2834              private:= "";
2835            fi;
2836            for i in record.out do
2837              if std = true or i[1] in std then
2838                entry:= [ i[2],
2839                          private,
2840                          String( i[1] ),
2841                          AGR.VersionOfSLP( i[3] ),
2842                          [ names[1], i[3], i[1] ] ];
2843                if toc.TocID <> "core" then
2844                  entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
2845                fi;
2846                Add( data, entry );
2847              fi;
2848            od;
2849          fi;
2850        fi;
2851      od;
2852
2853      return AGR.CommonDisplayPRG( "automorphisms", stdavail, data, false );
2854    end,
2855
2856    # entry: `[ <std>, <autname>, <file> ]',
2857    # conditions: `[ "automorphism", <autname> ]'
2858    #             or together with `[ "version", <vers> ]'
2859    AccessPRG := function( toc, groupname, std, conditions )
2860      local version, record, entry;
2861
2862      if not IsBound( toc.( groupname ) ) then
2863        return fail;
2864      elif Length( conditions ) = 2 and conditions[1] = "automorphism" then
2865        version:= true;
2866      elif Length( conditions ) = 4 and conditions[1] = "automorphism"
2867                                    and conditions[3] = "version" then
2868        version:= String( conditions[4] );
2869      else
2870        return fail;
2871      fi;
2872
2873      record:= toc.( groupname );
2874      if IsBound( record.out ) then
2875        for entry in record.out do
2876          if ( std = true or entry[1] in std ) and
2877             ( version = true or AGR.VersionOfSLP( entry[3] ) = version ) and
2878             entry[2] = conditions[2] then
2879            entry:= entry{ [ 3, 1 ] };
2880            if toc.TocID <> "core" then
2881              entry[1]:= [ [ toc.TocID, entry[1] ] ];
2882            fi;
2883            return entry;
2884          fi;
2885        od;
2886      fi;
2887      return fail;
2888    end,
2889
2890    # Create the program info from the identifier.
2891    AtlasProgramInfo := function( type, identifier, groupname )
2892    local filename, parsed;
2893
2894    filename:= identifier[2];
2895    if not IsString( filename ) then
2896      filename:= filename[1][2];
2897    fi;
2898
2899    if IsString( filename ) then
2900      parsed:= AGR.ParseFilenameFormat( filename, type[2].FilenameFormat );
2901      if parsed <> fail then
2902        return rec( standardization := identifier[3],
2903                    identifier      := identifier,
2904                    autname         := parsed[5] );
2905      fi;
2906    fi;
2907
2908    return fail;
2909    end,
2910
2911    # It would be good to check whether the order of the automorphism
2912    # fits to the name of the script, but the scripts do not describe
2913    # automorphisms of minimal possible order.
2914    # (So the power given by the name of the script is an inner
2915    # automorphism; how could we check this with reasonable effort?)
2916    # Thus we check just whether the name fits to the structure of the
2917    # outer automorphism group and to the order of the automorphism.
2918    # (We copy the relevant part of the code of `AGR.TestWordsSLPDefault'
2919    # into this function.)
2920    TestWords := function( tocid, name, file, type, verbose )
2921      local filename, prog, prg, gens, gapname, pos, claimedorder, tbl,
2922            outinfo, bound, imgs, order;
2923
2924      # Read the program.
2925      if tocid = "core" then
2926        tocid:= "dataword";
2927      fi;
2928      prog:= AGR.FileContents( [ [ tocid, file ] ], type );
2929      if prog = fail then
2930        Print( "#E  file `", file, "' is corrupted\n" );
2931        return false;
2932      fi;
2933
2934      # Check consistency.
2935      if prog = fail or not IsInternallyConsistent( prog.program ) then
2936        Print( "#E  program `", file, "' not internally consistent\n" );
2937        return false;
2938      fi;
2939      prg:= prog.program;
2940
2941      # Create the list of (trivial) generators.
2942      gens:= ListWithIdenticalEntries( NrInputsOfStraightLineProgram( prg ),
2943                                       () );
2944
2945      # Run the program.
2946      gens:= ResultOfStraightLineProgram( prg, gens );
2947
2948      # Get the GAP name of `name'.
2949      gapname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
2950                       pair -> name = pair[2] );
2951      if gapname = fail then
2952        Print( "#E  problem: no GAP name for `", name, "'\n" );
2953        return false;
2954      fi;
2955      gapname:= gapname[1];
2956
2957      # Get the order of the automorphism from the filename.
2958      pos:= PositionSublist( file, "-a" );
2959      claimedorder:= file{ [ pos+2 .. Length( file ) ] };
2960      pos:= Position( claimedorder, 'W' );
2961      claimedorder:= claimedorder{ [ 1 .. pos-1 ] };
2962      pos:= Position( claimedorder, 'p' );
2963      if pos <> fail then
2964	if not ForAll( claimedorder{ [ pos+1 .. Length( claimedorder ) ] },
2965	               IsDigitChar ) then
2966	  Print( "#E  wrong number of dashes in `", file, "'\n" );
2967	  return false;
2968	elif claimedorder{ [ pos+1 .. Length( claimedorder ) ] } = "0" then
2969          Print( "#E  wrong name `", file, "'\n" );
2970	  return false;
2971	fi;
2972        claimedorder:= claimedorder{ [ 1 .. pos-1 ] };
2973      fi;
2974      pos:= Position( claimedorder, '_' );
2975      if pos <> fail then
2976        claimedorder:= claimedorder{ [ 1 .. pos-1 ] };
2977      fi;
2978      if not ForAll( claimedorder, IsDigitChar ) then
2979        Print( "#E  wrong name `", file, "'\n" );
2980	return false;
2981      fi;
2982      claimedorder:= Int( claimedorder );
2983
2984      # Get the structure of the automorphism group.
2985      # If this group is cyclic then we compare orders.
2986      tbl:= CharacterTable( gapname );
2987      if tbl <> fail and IsBound( AGR.HasExtensionInfoCharacterTable )
2988                     and AGR.HasExtensionInfoCharacterTable( tbl ) then
2989        outinfo:= AGR.ExtensionInfoCharacterTable( tbl )[2];
2990        if    outinfo = "" then
2991          Print( "#E  automorphism `", file,
2992                 "' for group without outer automorphisms\n" );
2993          return false;
2994        elif outinfo <> "2" and claimedorder = 0 then
2995          Print( "#E  automorphism `", file,
2996                 "' but the outer automorphism is not unique\n" );
2997          return false;
2998        elif Int( outinfo ) <> fail and claimedorder <> 0
2999             and Int( outinfo ) mod claimedorder <> 0 then
3000          Print( "#E  automorphism `", file,
3001                 "' for outer automorphism group ", outinfo, "\n" );
3002          return false;
3003        fi;
3004      fi;
3005
3006      if claimedorder = 0 then
3007        claimedorder:= 2;
3008      fi;
3009
3010      # Get generators of the group in question.
3011      gens:= OneAtlasGeneratingSetInfo( gapname,
3012                 "contents", [ tocid, "local" ] );
3013      if gens <> fail and tbl <> fail then
3014        gens:= AtlasGenerators( gens );
3015        if gens <> fail then
3016          gens:= gens.generators;
3017          bound:= Exponent( tbl ) * claimedorder;
3018
3019          # Compute the order of the automorphism.
3020          imgs:= ResultOfStraightLineProgram( prg, gens );
3021          order:= 1;
3022          while order < bound and imgs <> gens do
3023            imgs:= ResultOfStraightLineProgram( prg, imgs );
3024            order:= order + 1;
3025          od;
3026
3027          if   imgs <> gens then
3028            Print( "#E  order ", order, " of automorphism `", file,
3029                   "' is larger than ", bound, "\n" );
3030            return false;
3031          elif order mod claimedorder <> 0 then
3032            Print( "#E  order ", order, " of automorphism `", file,
3033                   "' not divisible by ", claimedorder, "\n" );
3034            return false;
3035          fi;
3036        fi;
3037      fi;
3038
3039      return true;
3040    end,
3041
3042    # There is only one file.
3043    ReadAndInterpretDefault := paths -> ScanStraightLineProgram( paths[1] ),
3044    ) );
3045
3046
3047#############################################################################
3048##
3049#D  Straight line programs for switching between different standardizations
3050##
3051##  <#GAPDoc Label="type:switch:format">
3052##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-G</C><M>j</M><C>W</C><M>n</M></Mark>
3053##  <Item>
3054##    In this case, the file contains a straight line program that takes
3055##    generators of <M>G</M> w.&nbsp;r.&nbsp;t.&nbsp;the <M>i</M>-th set
3056##    of standard generators, and returns standard generators of <M>G</M>
3057##    w.&nbsp;r.&nbsp;t.&nbsp;the <M>j</M>-th set of standard generators.
3058##    An example is <C>L35G1-G2W1</C>.
3059##  </Item>
3060##  <#/GAPDoc>
3061##
3062AGR.DeclareDataType( "prg", "switch", rec(
3063    # `<groupname>G<i>-G<j>W<n>'
3064    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
3065                          [  "G", IsDigitChar, "W", IsDigitChar ] ],
3066                        [ ParseBackwards, ParseForwards ] ],
3067
3068    # `[ <i>, <j>, <filename> ]'
3069    AddFileInfo := function( list, entry, name )
3070      Add( list, [ entry[3], entry[5], name ] );
3071      return true;
3072    end,
3073
3074    DisplayPRG := function( tocs, names, std, stdavail )
3075      local data, toc, record, private, i, entry;
3076
3077      data:= [];
3078
3079      for toc in tocs do
3080        if IsBound( toc.( names[2] ) ) then
3081          record:= toc.( names[2] );
3082          if IsBound( record.switch ) then
3083            if toc.TocID <> "core" then
3084              private:= UserPreference( "AtlasRep",
3085                            "AtlasRepMarkNonCoreData" );
3086            else
3087              private:= "";
3088            fi;
3089            for i in record.switch do
3090              if std = true or i[1] in std then
3091                entry:= [ Concatenation( String( i[1] ), " -> ",
3092                                         String( i[2] ) ),
3093                          private,
3094                          String( i[1] ),
3095                          AGR.VersionOfSLP( i[3] ),
3096                          [ names[1], i[3], i[1] ] ];
3097                if toc.TocID <> "core" then
3098                  entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
3099                fi;
3100                Add( data, entry );
3101              fi;
3102            od;
3103          fi;
3104        fi;
3105      od;
3106
3107      return AGR.CommonDisplayPRG( "restandardizations", stdavail, data, false );
3108    end,
3109
3110    # entry: `[ <std>, <descr>, <file> ]',
3111    # conditions: `[ "restandardize", <std2> ]'
3112    #             or together with `[ "version", <vers> ]'
3113    AccessPRG := function( toc, groupname, std, conditions )
3114      local version, record, entry;
3115
3116      if not IsBound( toc.( groupname ) ) then
3117        return fail;
3118      elif Length( conditions ) = 2 and conditions[1] = "restandardize" then
3119        version:= true;
3120      elif Length( conditions ) = 4 and conditions[1] = "restandardize"
3121                                    and conditions[3] = "version" then
3122        version:= String( conditions[4] );
3123      else
3124        return fail;
3125      fi;
3126      record:= toc.( groupname );
3127
3128      if IsBound( record.switch ) then
3129        for entry in record.switch do
3130          if ( std = true or entry[1] in std ) and
3131             ( version = true or AGR.VersionOfSLP( entry[3] ) = version ) and
3132             conditions[2] = entry[2] then
3133            entry:= entry{ [ 3, 1, 2 ] };
3134            if toc.TocID <> "core" then
3135              entry[1]:= [ [ toc.TocID, entry[1] ] ];
3136            fi;
3137            return entry;
3138          fi;
3139        od;
3140      fi;
3141      return fail;
3142    end,
3143
3144    TestWords := function( tocid, name, file, type, verbose )
3145      return AGR.TestWordsSLPDefault( tocid, name, file, type, false, verbose );
3146    end,
3147
3148    # There is only one file.
3149    ReadAndInterpretDefault := paths -> ScanStraightLineProgram( paths[1] ),
3150    ) );
3151
3152
3153#############################################################################
3154##
3155#D  Black box programs for finding standard generators
3156##
3157##  <#GAPDoc Label="type:find:format">
3158##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-find</C><M>n</M></Mark>
3159##  <Item>
3160##    <Index Subkey="for finding standard generators">black box program
3161##    </Index>
3162##    In this case, the file contains a black box program that takes
3163##    a group, and returns (if it is successful) a set of standard generators
3164##    for <M>G</M>, w.&nbsp;r.&nbsp;t.&nbsp;the <M>i</M>-th standardization.
3165##  </Item>
3166##  <#/GAPDoc>
3167##
3168AGR.DeclareDataType( "prg", "find", rec(
3169    # `<groupname>G<i>-find<j>'
3170    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
3171                          [ "find", IsDigitChar ] ],
3172                        [ ParseBackwards, ParseBackwardsWithPrefix ] ],
3173
3174    # `[ <i>, <j>, <filename> ]'
3175    AddFileInfo := function( list, entry, name )
3176      Add( list, [ entry[3], entry[5], name ] );
3177      return true;
3178    end,
3179
3180    DisplayOverviewInfo := AGR.DisplayOverviewInfoDefault( "fnd", "c", "find" ),
3181
3182    DisplayPRG := function( tocs, names, std, stdavail )
3183      local data, toc, record, private, i, entry;
3184
3185      data:= [];
3186
3187      for toc in tocs do
3188        if IsBound( toc.( names[2] ) ) then
3189          record:= toc.( names[2] );
3190          if IsBound( record.find ) then
3191            if toc.TocID <> "core" then
3192              private:= UserPreference( "AtlasRep",
3193                            "AtlasRepMarkNonCoreData" );
3194            else
3195              private:= "";
3196            fi;
3197            for i in record.find do
3198              if std = true or i[1] in std then
3199                entry:= [ "",
3200                          private,
3201                          String( i[1] ),
3202                          String( i[2] ),
3203                          [ names[1], i[3], i[1] ] ];
3204                if toc.TocID <> "core" then
3205                  entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
3206                fi;
3207                Add( data, entry );
3208              fi;
3209            od;
3210          fi;
3211        fi;
3212      od;
3213
3214      return AGR.CommonDisplayPRG( "std. gen. finder", stdavail, data, true );
3215    end,
3216
3217    # entry: `[ <std>, <version>, <file> ]',
3218    # conditions: `[ "find" ]'
3219    #             or together with `[ "version", <vers> ]'
3220    AccessPRG := function( toc, groupname, std, conditions )
3221      local version, record, entry;
3222
3223      if not IsBound( toc.( groupname ) ) then
3224        return fail;
3225      elif Length( conditions ) = 1 and conditions[1] = "find" then
3226        version:= true;
3227      elif Length( conditions ) = 3 and conditions[1] = "find"
3228                                    and conditions[2] = "version" then
3229        version:= String( conditions[3] );
3230      else
3231        return fail;
3232      fi;
3233
3234      record:= toc.( groupname );
3235      if IsBound( record.find ) then
3236        for entry in record.find do
3237          if ( std = true or entry[1] in std ) and
3238             ( version = true or AGR.VersionOfSLP( entry[3] ) = version ) then
3239            # the part of the identifier
3240            entry:= entry{ [ 3, 1, 2 ] };
3241            if toc.TocID <> "core" then
3242              entry[1]:= [ [ toc.TocID, entry[1] ] ];
3243            fi;
3244            return entry;
3245          fi;
3246        od;
3247      fi;
3248      return fail;
3249    end,
3250
3251    # There is only one file.
3252    ReadAndInterpretDefault := paths -> ScanBBoxProgram( AGR.StringFile(
3253                                                             paths[1] ) ),
3254
3255    # If there is a representation for this group (independent of the
3256    # standardization) then we apply the script, and check whether at least
3257    # the whole group is generated by the result; if also a `check' script
3258    # is available for this standardization then we run it on the result.
3259    TestWords := function( tocid, name, file, type, verbose )
3260      local prog, prg, gapname, gens, G, res, pos, pos2, std, check;
3261
3262      # Read the program.
3263      if tocid = "core" then
3264        tocid:= "dataword";
3265      fi;
3266      prog:= AGR.FileContents( [ [ tocid, file ] ], type );
3267      if prog = fail then
3268        Print( "#E  file `", file, "' is corrupted\n" );
3269        return false;
3270      fi;
3271      prg:= prog.program;
3272
3273      # Get the GAP name of `name'.
3274      gapname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
3275                       pair -> name = pair[2] );
3276      if gapname = fail then
3277        Print( "#E  problem: no GAP name for `", name, "'\n" );
3278        return false;
3279      fi;
3280
3281      # Get generators of the group in question.
3282      gens:= OneAtlasGeneratingSetInfo( gapname[1], "contents", "local" );
3283      if gens <> fail then
3284        gens:= AtlasGenerators( gens );
3285        if gens <> fail then
3286          gens:= gens.generators;
3287          G:= Group( gens );
3288          if IsBound( gapname[3].size ) then
3289            SetSize( G, gapname[3].size );
3290          fi;
3291          res:= ResultOfBBoxProgram( prg, G );
3292          if IsList( res ) and not IsString( res ) then
3293            # Compute the standardization.
3294            pos:= Position( file, '-' );
3295            pos2:= pos - 1;
3296            while file[ pos2 ] <> 'G' do
3297              pos2:= pos2-1;
3298            od;
3299            std:= Int( file{ [ pos2+1 .. pos-1 ] } );
3300            check:= AtlasProgram( gapname[1], std, "check" );
3301            if check <> fail then
3302              if not ResultOfStraightLineDecision( check.program, res ) then
3303                Print( "#E  return values of `", file,
3304                       "' do not fit to the check file\n" );
3305                return false;
3306              fi;
3307            fi;
3308            # Check the group order only for permutation groups.
3309            if IsPermGroup( G ) then
3310              if not IsSubset( G, res ) then
3311                Print( "#E  return values of `", file,
3312                       "' do not lie in the group\n" );
3313                return false;
3314              elif Size( SubgroupNC( G, res ) ) <> Size( G ) then
3315                Print( "#E  return values of `", file,
3316                       "' do not generate the group\n" );
3317                return false;
3318              fi;
3319            fi;
3320          fi;
3321        fi;
3322      fi;
3323
3324      return true;
3325    end,
3326
3327    ) );
3328
3329
3330#############################################################################
3331##
3332#D  Straight line programs for checking standard generators
3333##
3334##  <#GAPDoc Label="type:check:format">
3335##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-check</C><M>n</M></Mark>
3336##  <Item>
3337##    <Index>semi-presentation</Index>
3338##    In this case, the file contains a straight line decision that takes
3339##    generators of <M>G</M>, and returns <K>true</K> if these generators are
3340##    standard generators w.&nbsp;r.&nbsp;t.&nbsp;the <M>i</M>-th
3341##    standardization, and <K>false</K> otherwise.
3342##  </Item>
3343##  <#/GAPDoc>
3344##
3345AGR.DeclareDataType( "prg", "check", rec(
3346    # `<groupname>G<i>-check<j>'
3347    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
3348                          [ "check", IsDigitChar ] ],
3349                        [ ParseBackwards, ParseBackwardsWithPrefix ] ],
3350
3351    # `[ <i>, <j>, <filename> ]'
3352    AddFileInfo := function( list, entry, name )
3353      Add( list, [ entry[3], entry[5], name ] );
3354      return true;
3355    end,
3356
3357    DisplayOverviewInfo := [ "chk", "c", function( conditions )
3358      local groupname, tocs, std, value, private, toc, record;
3359
3360      groupname:= conditions[1][2];
3361      tocs:= AGR.TablesOfContents( conditions );
3362      if Length( conditions ) = 1 or
3363         not ( IsInt( conditions[2] ) or IsList( conditions[2] ) ) then
3364        std:= true;
3365      else
3366        std:= conditions[2];
3367        if IsInt( std ) then
3368          std:= [ std ];
3369        fi;
3370      fi;
3371
3372      value:= "";
3373      private:= false;
3374      for toc in tocs do
3375        if IsBound( toc.( groupname ) ) then
3376          record:= toc.( groupname );
3377          if ( IsBound( record.check ) and
3378               ForAny( record.check, x -> std = true or x[1] in std ) ) or
3379             ( IsBound( record.pres ) and
3380               ForAny( record.pres, x -> std = true or x[1] in std ) ) then
3381            value:= "+";
3382            if toc.TocID <> "core" then
3383              private:= true;
3384            fi;
3385            break;
3386          fi;
3387        fi;
3388      od;
3389      return [ value, private ];
3390    end ],
3391
3392    DisplayPRG := function( tocs, names, std, stdavail )
3393      local data, toc, record, private, comp, i, entry;
3394
3395      data:= [];
3396
3397      for toc in tocs do
3398        if IsBound( toc.( names[2] ) ) then
3399          record:= toc.( names[2] );
3400          if toc.TocID <> "core" then
3401            private:= UserPreference( "AtlasRep",
3402                          "AtlasRepMarkNonCoreData" );
3403          else
3404            private:= "";
3405          fi;
3406          for comp in [ "check", "pres" ] do
3407            if IsBound( record.( comp ) ) then
3408              for i in record.( comp ) do
3409                if std = true or i[1] in std then
3410                  entry:= [ Concatenation( "(", comp, ")" ),
3411                            private,
3412                            String( i[1] ),
3413                            String( i[2] ),
3414                            [ names[1], i[3], i[1] ] ];
3415                  if toc.TocID <> "core" then
3416                    entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
3417                  fi;
3418                  Add( data, entry );
3419                fi;
3420              od;
3421            fi;
3422          od;
3423        fi;
3424      od;
3425
3426      return AGR.CommonDisplayPRG( "std. gen. checker", stdavail, data, true );
3427    end,
3428
3429    # entry: `[ <std>, <version>, <file> ]',
3430    # conditions: `[ "check" ]'
3431    #             or together with `[ "version", <vers> ]'
3432    AccessPRG := function( toc, groupname, std, conditions )
3433      local version, record, entry, comp;
3434
3435      if not IsBound( toc.( groupname ) ) then
3436        return fail;
3437      elif Length( conditions ) = 1 and conditions[1] = "check" then
3438        version:= true;
3439      elif Length( conditions ) = 3 and conditions[1] = "check"
3440                                    and conditions[2] = "version" then
3441        version:= String( conditions[3] );
3442      else
3443        return fail;
3444      fi;
3445      record:= toc.( groupname );
3446
3447      for comp in [ "check", "pres" ] do
3448        if IsBound( record.( comp ) ) then
3449          for entry in record.( comp ) do
3450            if ( std = true or entry[1] in std ) and
3451               ( version = true or AGR.VersionOfSLP( entry[3] ) = version ) then
3452              # the part of the identifier
3453              entry:= entry{ [ 3, 1, 2 ] };
3454              if toc.TocID <> "core" then
3455                entry[1]:= [ [ toc.TocID, entry[1] ] ];
3456              fi;
3457              return entry;
3458            fi;
3459          od;
3460        fi;
3461      od;
3462      return fail;
3463    end,
3464
3465    TestWords := function( tocid, name, file, type, verbose )
3466        return AGR.TestWordsSLDDefault( tocid, name, file, type,
3467                 [ IsChar, "G", IsDigitChar, "-check", IsDigitChar ],
3468                 verbose ); end,
3469
3470    # There is only one file.
3471    ReadAndInterpretDefault := paths -> ScanStraightLineDecision(
3472                                            AGR.StringFile( paths[1] ) ),
3473    ) );
3474
3475
3476#############################################################################
3477##
3478#D  Straight line decisions representing presentations
3479##
3480##  <#GAPDoc Label="type:pres:format">
3481##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-P</C><M>n</M></Mark>
3482##  <Item>
3483##    <Index>presentation</Index>
3484##    In this case, the file contains a straight line decision that takes
3485##    some group elements, and returns <K>true</K> if these elements are
3486##    standard generators for <M>G</M>,
3487##    w.&nbsp;r.&nbsp;t.&nbsp;the <M>i</M>-th standardization,
3488##    and <K>false</K> otherwise.
3489##  </Item>
3490##  <#/GAPDoc>
3491##
3492AGR.DeclareDataType( "prg", "pres", rec(
3493    # `<groupname>G<i>-P<j>'
3494    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
3495                          [ "P", IsDigitChar ] ],
3496                        [ ParseBackwards, ParseBackwardsWithPrefix ] ],
3497
3498    # `[ <i>, <j>, <filename> ]'
3499    AddFileInfo := function( list, entry, name )
3500      Add( list, [ entry[3], entry[5], name ] );
3501      return true;
3502    end,
3503
3504    DisplayOverviewInfo := AGR.DisplayOverviewInfoDefault( "prs", "c", "pres" ),
3505
3506    DisplayPRG := function( tocs, names, std, stdavail )
3507      local data, toc, record, private, i, entry;
3508
3509      data:= [];
3510
3511      for toc in tocs do
3512        if IsBound( toc.( names[2] ) ) then
3513          record:= toc.( names[2] );
3514          if IsBound( record.pres ) then
3515            if toc.TocID <> "core" then
3516              private:= UserPreference( "AtlasRep",
3517                            "AtlasRepMarkNonCoreData" );
3518            else
3519              private:= "";
3520            fi;
3521            for i in record.pres do
3522              if std = true or i[1] in std then
3523                entry:= [ "",
3524                          private,
3525                          String( i[1] ),
3526                          String( i[2] ),
3527                          [ names[1], i[3], i[1] ] ];
3528                if toc.TocID <> "core" then
3529                  entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
3530                fi;
3531                Add( data, entry );
3532              fi;
3533            od;
3534          fi;
3535        fi;
3536      od;
3537
3538      return AGR.CommonDisplayPRG( "presentation", stdavail, data, true );
3539    end,
3540
3541    # entry: `[ <std>, <version>, <file> ]',
3542    # conditions: `[ "presentation" ]'
3543    #             or together with `[ "version", <vers> ]'
3544    AccessPRG := function( toc, groupname, std, conditions )
3545      local version, record, entry;
3546
3547      if not IsBound( toc.( groupname ) ) then
3548        return fail;
3549      elif Length( conditions ) = 1 and conditions[1] = "presentation" then
3550        version:= true;
3551      elif Length( conditions ) = 3 and conditions[1] = "presentation"
3552                                    and conditions[2] = "version" then
3553        version:= String( conditions[3] );
3554      else
3555        return fail;
3556      fi;
3557      record:= toc.( groupname );
3558
3559      if IsBound( record.pres ) then
3560        for entry in record.pres do
3561          if ( std = true or entry[1] in std ) and
3562             ( version = true or AGR.VersionOfSLP( entry[3] ) = version ) then
3563            # the part of the identifier
3564            entry:= entry{ [ 3, 1, 2 ] };
3565            if toc.TocID <> "core" then
3566              entry[1]:= [ [ toc.TocID, entry[1] ] ];
3567            fi;
3568            return entry;
3569          fi;
3570        od;
3571      fi;
3572      return fail;
3573    end,
3574
3575    TestWords := function( tocid, name, file, type, verbose )
3576        return AGR.TestWordsSLDDefault( tocid, name, file, type,
3577                 [ IsChar, "G", IsDigitChar, "-P", IsDigitChar ],
3578                 verbose ); end,
3579
3580    # There is only one file.
3581    ReadAndInterpretDefault := paths -> ScanStraightLineDecision(
3582                                            AGR.StringFile( paths[1] ) ),
3583    ) );
3584
3585
3586#############################################################################
3587##
3588#D  Other straight line programs
3589##
3590##  <#GAPDoc Label="type:otherscripts:format">
3591##  <Mark><M>groupname</M><C>G</C><M>i</M><C>-X</C><M>descr</M><C>W</C><M>n</M></Mark>
3592##  <Item>
3593##    In this case, the file contains a straight line program that takes
3594##    generators of <M>G</M> w.&nbsp;r.&nbsp;t.&nbsp;the <M>i</M>-th set
3595##    of standard generators,
3596##    and whose return value corresponds to <M>descr</M>.
3597##    This format is used only in private extensions
3598##    (see Chapter&nbsp;<Ref Chap="chap:Private Extensions"/>),
3599##    such a script can be accessed with <M>descr</M> as the third argument
3600##    of <Ref Func="AtlasProgram"/>.
3601##  </Item>
3602##  <#/GAPDoc>
3603##
3604AGR.DeclareDataType( "prg", "otherscripts", rec(
3605
3606    # `<groupname>G<i>-X<descr>W<n>'
3607    FilenameFormat := [ [ [ IsChar, "G", IsDigitChar ],
3608                          [ "X", IsChar, "W", IsDigitChar ] ],
3609                        [ ParseBackwards, ParseBackwardsWithPrefix ] ],
3610
3611    # `[ <i>, <descr>, <filename> ]'
3612    AddFileInfo := function( list, entry, name )
3613      Add( list, Concatenation( entry{ [ 3, 5 ] }, [ name ] ) );
3614      return true;
3615    end,
3616
3617    DisplayPRG := function( tocs, names, std, stdavail )
3618      local data, toc, record, private, i, entry;
3619
3620      data:= [];
3621
3622      for toc in tocs do
3623        if IsBound( toc.( names[2] ) ) then
3624          record:= toc.( names[2] );
3625          if IsBound( record.otherscripts ) then
3626            if toc.TocID <> "core" then
3627              private:= UserPreference( "AtlasRep",
3628                            "AtlasRepMarkNonCoreData" );
3629            else
3630              private:= "";
3631            fi;
3632            for i in record.otherscripts do
3633              if std = true or i[1] in std then
3634                entry:= [ Concatenation( "\"", i[2], "\"" ),
3635                          private,
3636                          String( i[1] ),
3637                          AGR.VersionOfSLP( i[3] ),
3638                          [ names[1], i[3], i[1] ] ];
3639                if toc.TocID <> "core" then
3640                  entry[5][2]:= [ [ toc.TocID, entry[5][2] ] ];
3641                fi;
3642                Add( data, entry );
3643              fi;
3644            od;
3645          fi;
3646        fi;
3647      od;
3648
3649      return AGR.CommonDisplayPRG( "other scripts", stdavail, data, false );
3650    end,
3651
3652    # entry: `[ <std>, <descr>, <file> ]',
3653    # conditions: `[ "other", <descr> ]'
3654    #             or together with `[ "version", <vers> ]'
3655    AccessPRG := function( toc, groupname, std, conditions )
3656      local version, record, entry;
3657
3658      if not IsBound( toc.( groupname ) ) then
3659        return fail;
3660      elif Length( conditions ) = 2 and conditions[1] = "other" then
3661        version:= true;
3662      elif Length( conditions ) = 4 and conditions[1] = "other"
3663                                    and conditions[3] = "version" then
3664        version:= String( conditions[4] );
3665      else
3666        return fail;
3667      fi;
3668
3669      record:= toc.( groupname );
3670      if IsBound( record.otherscripts ) then
3671        for entry in record.otherscripts do
3672          if ( std = true or entry[1] in std ) and
3673             ( version = true or AGR.VersionOfSLP( entry[3] ) = version ) and
3674             entry[2] = conditions[2] then
3675            entry:= entry{ [ 3, 1 ] };
3676            if toc.TocID <> "core" then
3677              entry[1]:= [ [ toc.TocID, entry[1] ] ];
3678            fi;
3679            return entry;
3680          fi;
3681        od;
3682      fi;
3683      return fail;
3684    end,
3685
3686    TestWords := function( tocid, name, file, type, verbose )
3687      return AGR.TestWordsSLPDefault( tocid, name, file, type, false, verbose );
3688    end,
3689
3690    # There is only one file.
3691    ReadAndInterpretDefault := paths -> ScanStraightLineProgram( paths[1] ),
3692    ) );
3693
3694
3695#############################################################################
3696##
3697##  Read the known tables of contents,
3698##  as given by the user preference "AtlasRepTOCData".
3699##
3700##  Note that the current file gets notified via
3701##  'DeclareAutoreadableVariables',
3702##  because we want to delay the evaluation of the data.
3703##
3704##  We cannot read the tables of contents in 'read.g' because this would
3705##  trigger that 'gap/types.g' and then 'atlasprm.json' etc. are read.
3706##  This would not work because some functions are not yet available in this
3707##  situation.
3708##
3709##  (A notification of the "internal" extension in test mode is contained
3710##  in the test file 'tst/atlasrep.tst'.)
3711##
3712AGR.EvaluateTOC:= function()
3713    local entry, pos, id, filename;
3714
3715    for entry in UserPreference( "AtlasRep", "AtlasRepTOCData" ) do
3716      pos:= Position( entry, '|' );
3717      if pos <> fail then
3718        id:= entry{ [ 1 .. pos-1 ] };
3719        filename:= entry{ [ pos+1 .. Length( entry ) ] };
3720        AtlasOfGroupRepresentationsNotifyData( filename, id );
3721      fi;
3722    od;
3723end;
3724
3725AGR.EvaluateTOC();
3726
3727
3728#############################################################################
3729##
3730##  For backwards compatibility, we set the components of the global record
3731##  'AtlasOfGroupRepresentationsInfo' that were used up to version 1.5.1
3732##  of the package, for specifying user preferences.
3733##  Note that the values of the "real" user preferences
3734##  are relevant for setting the record components,
3735##  modifying the record components does *not* affect these user preferences.
3736##
3737##  (We cannot move the code to 'obsolete.gi' because then 'types.g' would
3738##  be read too early.)
3739##
3740if UserPreference( "gap", "ReadObsolete" ) <> false then
3741  AtlasOfGroupRepresentationsInfo.SetComponentsOfUserParameters:= function()
3742    local url, pos, wget;
3743
3744    AtlasOfGroupRepresentationsInfo.remote:= UserPreference( "AtlasRep",
3745        "AtlasRepAccessRemoteFiles" );
3746
3747    url:= First( AtlasOfGroupRepresentationsInfo.notified,
3748                 r -> r.ID = "core" ).DataURL;
3749    if 7 < Length( url ) and
3750       LowercaseString( url{ [ 1 .. 7 ] } ) = "http://" then
3751      url:= url{ [ 8 .. Length( url ) ] };
3752    fi;
3753    pos:= Position( url, '/' );
3754    AtlasOfGroupRepresentationsInfo.servers:= [
3755        [ url{ [ 1 .. pos - 1 ] }, url{ [ pos+1 .. Length( url ) ] } ] ];
3756
3757    wget:= UserPreference( "AtlasRep", "FileTransferTool" );
3758    if wget = "wget" then
3759      AtlasOfGroupRepresentationsInfo.wget:= true;
3760    elif wget = "io" then
3761      AtlasOfGroupRepresentationsInfo.wget:= false;
3762    else
3763      AtlasOfGroupRepresentationsInfo.wget:= "prefer IO to wget";
3764    fi;
3765
3766    AtlasOfGroupRepresentationsInfo.compress:= UserPreference( "AtlasRep",
3767        "CompressDownloadedMeatAxeFiles" );
3768
3769    AtlasOfGroupRepresentationsInfo.displayFunction:= EvalString(
3770        UserPreference( "AtlasRep", "DisplayFunction" ) );
3771
3772    AtlasOfGroupRepresentationsInfo.markprivate:= UserPreference( "AtlasRep",
3773        "AtlasRepMarkNonCoreData" );
3774
3775    end;
3776
3777  AtlasOfGroupRepresentationsInfo.SetComponentsOfUserParameters();
3778fi;
3779
3780
3781#############################################################################
3782##
3783#E
3784
3785