1#############################################################################
2##
3#W  types.gi             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 functions for administrating
8##  the data types used in the ATLAS of Group Representations.
9##
10
11
12#############################################################################
13##
14#F  TOCEntryStringDefault( <typename>, <entry> )
15##
16BindGlobal( "TOCEntryStringDefault", function( typename, entry )
17    local list, name, pos, info, crc;
18
19    list:= AtlasOfGroupRepresentationsInfo.filenames;
20    name:= entry[ Length( entry ) ];
21    pos:= PositionSorted( list, [ name ] );
22    if pos <= Length( list ) and list[ pos ][1] = name then
23      entry:= list[ pos ];
24      if Length( entry ) < 4 then
25        # There is no crc value yet.
26        # If the file is available locally then compute the value,
27        # otherwise leave it out.
28        info:= AtlasOfGroupRepresentationsLocalFilename( [ [ entry[3], entry[1] ] ], typename );
29        info:= First( info, l -> l[2][1][2] = true );
30        if info <> fail then
31          crc:= CrcFile( info[2][1][1] );
32        else
33          crc:= fail;
34        fi;
35      else
36        crc:= entry[4];
37      fi;
38      info:= Concatenation( "\"", typename, "\",\"", entry[2], "\"" );
39      if crc <> fail then
40        Append( info, Concatenation( ",[", String( crc ), "]" ) );
41      fi;
42      return info;
43    else
44      return fail;
45    fi;
46end );
47
48
49#############################################################################
50##
51#F  AGR.DisplayOverviewInfoDefault( <dispname>, <align>, <compname> )
52##
53AGR.DisplayOverviewInfoDefault:= function( dispname, align, compname )
54    return [ dispname, align, function( conditions )
55      local groupname, tocs, std, value, private, toc, record, new;
56
57      groupname:= conditions[1][2];
58      tocs:= AGR.TablesOfContents( conditions );
59      if Length( conditions ) = 1 or
60         not ( IsInt( conditions[2] ) or IsList( conditions[2] ) ) then
61        std:= true;
62      else
63        std:= conditions[2];
64        if IsInt( std ) then
65          std:= [ std ];
66        fi;
67      fi;
68
69      value:= false;
70      private:= false;
71      for toc in tocs do
72        if IsBound( toc.( groupname ) ) then
73          record:= toc.( groupname );
74          if IsBound( record.( compname ) ) then
75            new:= ForAny( record.( compname ),
76                          x -> std = true or x[1] in std );
77            if toc.TocID <> "core" and new then
78              private:= true;
79            fi;
80            value:= value or new;
81          fi;
82        fi;
83      od;
84      if value then
85        value:= "+";
86      else
87        value:= "";
88      fi;
89      return [ value, private ];
90    end ];
91    end;
92
93
94#############################################################################
95##
96#F  AGR.TestWordsSLPDefault( <tocid>, <name>, <file>, <type>, <outputs>,
97#F                           <verbose> )
98##
99##  For the straight line program that is returned by
100##  <Ref Func="AGR.FileContents"/> when this is called
101##  with the first four arguments,
102##  it is checked that it is internally consistent and that it can be
103##  evaluated at the right number of arguments.
104##  If the argument <A>outputs</A> is <K>true</K> then it is additionally
105##  checked that the result record has a component <C>outputs</C>,
106##  a list whose length equals the number of outputs of the program.
107##  (The argument <A>verbose</A> is currently not used,
108##  in other <C>TestWords</C> functions the value <K>true</K> triggers that
109##  more statements may be printed than just error messages.
110##
111AGR.TestWordsSLPDefault:= function( tocid, name, file, type, outputs, verbose )
112    local filename, prog, prg, gens;
113
114    # Read the program.
115    if tocid = "core" then
116      tocid:= "dataword";
117    fi;
118    prog:= AGR.FileContents( [ [ tocid, file ] ], type );
119    if prog = fail then
120      Print( "#E  file `", file, "' is corrupted\n" );
121      return false;
122    fi;
123
124    # Check consistency.
125    if prog = fail or not IsInternallyConsistent( prog.program ) then
126      Print( "#E  program `", file, "' not internally consistent\n" );
127      return false;
128    fi;
129    prg:= prog.program;
130
131    # Create the list of (trivial) generators.
132    gens:= ListWithIdenticalEntries( NrInputsOfStraightLineProgram( prg ),
133                                     () );
134
135    # Run the program.
136    gens:= ResultOfStraightLineProgram( prg, gens );
137
138    # If the script computes class representatives then
139    # check whether there is an `outputs' component of the right length.
140    if outputs = true then
141      if not IsBound( prog.outputs ) then
142        Print( "#E  program `", file, "' without component `outputs'\n" );
143        return false;
144      elif Length( prog.outputs ) <> Length( gens ) then
145        Print( "#E  program `", file, "' with wrong number of `outputs'\n" );
146        return false;
147      fi;
148    fi;
149
150    return true;
151    end;
152
153
154#############################################################################
155##
156#F  AGR.TestWordsSLDDefault( <tocid>, <name>, <file>, <type>, <format>,
157#F                           <verbose> )
158##
159##  For the straight line decision that is returned by
160##  <Ref Func="AGR.FileContents"/> when this is called
161##  with the same arguments,
162##  it is checked that it is internally consistent and that it can be
163##  evaluated in all relevant representations.
164##
165AGR.TestWordsSLDDefault:= function( tocid, name, file, type, format, verbose )
166    local filename, prog, result, gapname, orderfunc, std, entry, gens;
167
168    # Read the program.
169    if tocid = "core" then
170      tocid:= "dataword";
171    fi;
172    prog:= AGR.FileContents( [ [ tocid, file ] ], type );
173    if prog = fail then
174      Print( "#E  file `", file, "' is corrupted\n" );
175      return false;
176    fi;
177
178    # Check consistency.
179    if not IsInternallyConsistent( prog.program ) then
180      Print( "#E  program `", file, "' not internally consistent\n" );
181      return false;
182    fi;
183    prog:= prog.program;
184
185    # Evaluate the program in *all* relevant representations.
186    result:= true;
187    gapname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
188                     pair -> name = pair[2] );
189    if gapname = fail then
190      Print( "#E  problem: no GAP name for `", name, "'\n" );
191      return false;
192    fi;
193    gapname:= gapname[1];
194
195    orderfunc:= function( g )
196      if IsMatrix( g ) then
197        # We know that the group elements that occur have small order.
198        return OrderMatTrial( g, 10000 );
199      else
200        return Order( g );
201      fi;
202    end;
203
204    std:= ParseBackwards( file, format );
205    std:= std[3];
206    for entry in AllAtlasGeneratingSetInfos( gapname, std,
207                                             "contents", "local" ) do
208      gens:= AtlasGenerators( entry.identifier );
209      if gens <> fail then
210        if not ResultOfStraightLineDecision( prog, gens.generators,
211                   orderfunc ) then
212          Print( "#E  program `", file, "' does not fit to\n#E  `",
213                 entry.identifier, "'\n" );
214          result:= false;
215        fi;
216      fi;
217    od;
218
219    return result;
220    end;
221
222
223#############################################################################
224##
225#F  AGR.TestFileHeadersDefault( <tocid>, <groupname>, <entry>, <type>, <dim>,
226#F                              <special> )
227##
228##  This function can be used only if the last entry in the list <entry> is
229##  the name of *one* file that contains a list of matrices.
230##
231AGR.TestFileHeadersDefault:= function( tocid, groupname, entry, type, dim, special )
232    local filename, cand, name, mats;
233
234    if tocid = "core" then
235      tocid:= "datagens";
236    fi;
237
238    # Try to read the file.
239    dim:= [ dim, dim ];
240    filename:= entry[ Length( entry ) ];
241    cand:= AtlasOfGroupRepresentationsLocalFilename(
242               [ [ tocid, filename ] ], type );
243    if not ( Length( cand ) = 1 and ForAll( cand[1][2], x -> x[2] ) ) then
244      return true;
245    fi;
246    mats:= AGR.FileContents( [ [ tocid, filename ] ], type );
247
248    # Check that the file contains a list of matrices of the right dimension.
249    if   mats = fail then
250      Print( "#E  filename `", filename, "' not found\n" );
251      return false;
252    elif not ( IsList( mats ) and ForAll( mats, IsMatrix ) ) then
253      Print( "#E  file `", filename,
254             "' does not contain a list of matrices\n" );
255      return false;
256    elif ForAny( mats, mat -> DimensionsMat( mat ) <> dim ) then
257      Print( "#E  matrices in `",filename,"' have wrong dimensions\n" );
258      return false;
259    fi;
260
261    # Check the entries.
262    special:= special( entry, mats, filename );
263    if IsString( special ) then
264      Print( "#E  ", special, "\n" );
265      return false;
266    fi;
267
268    return true;
269    end;
270
271
272#############################################################################
273##
274#F  AGR.TestFilesMTX( <tocid>, <groupname>, <entry>, <type> )
275##
276AGR.TestFilesMTX:= function( tocid, groupname, entry, type )
277    local result, filename;
278
279    if tocid = "core" then
280      tocid:= "datagens";
281    fi;
282
283    # Read the file(s).
284    result:= true;
285    for filename in entry[ Length( entry ) ] do
286      if AGR.FileContents( [ [ tocid, filename ] ], type ) = fail then
287        Print( "#E  file `", filename, "' corrupted\n" );
288        result:= false;
289      fi;
290    od;
291
292    return result;
293    end;
294
295
296#############################################################################
297##
298#F  AtlasProgramInfoDefault( <type>, <identifier>, <groupname> )
299##
300##  This function can be used only in the case that the second entry in
301##  <identifier> is either one filename or a list of length one,
302##  such that the unique entry is a list of a t.o.c. identifier and
303##  a filename.
304##
305BindGlobal( "AtlasProgramInfoDefault",
306    function( type, identifier, groupname )
307    local filename;
308
309    filename:= identifier[2];
310    if not IsString( filename ) then
311      filename:= filename[1][2];
312    fi;
313
314    if IsString( filename ) and
315       AGR.ParseFilenameFormat( filename, type[2].FilenameFormat )
316           <> fail then
317      return rec( standardization := identifier[3],
318                  identifier      := identifier );
319    fi;
320
321    return fail;
322end );
323
324
325#############################################################################
326##
327#F  AtlasProgramDefault( <type>, <identifier>, <groupname> )
328##
329##  This function can be used only in the case that the second entry in
330##  <identifier> is either one filename or a list of length one,
331##  such that the unique entry is a list of a t.o.c. identifier and
332##  a filename.
333##
334BindGlobal( "AtlasProgramDefault", function( type, identifier, groupname )
335    local filename, tocid, prog, result;
336
337    filename:= identifier[2];
338    if IsString( filename ) then
339      tocid:= "dataword";
340    else
341      tocid:= filename[1][1];
342      filename:= filename[1][2];
343    fi;
344
345    if IsString( filename ) and
346       AGR.ParseFilenameFormat( filename, type[2].FilenameFormat )
347           <> fail then
348      prog:= AGR.FileContents( [ [ tocid, filename ] ], type );
349      if prog <> fail then
350        result:= rec( program         := prog.program,
351                      standardization := identifier[3],
352                      identifier      := identifier );
353        if IsBound( prog.outputs ) then
354          result.outputs:= prog.outputs;
355        fi;
356        return result;
357      fi;
358    fi;
359
360    return fail;
361end );
362
363
364#############################################################################
365##
366#F  AGR.CheckOneCondition( <func>[, <detect>], <condlist> )
367##
368##  This function always returns `true'; it changes <condlist> in place.
369##
370AGR.CheckOneCondition:= function( arg )
371    local func, detect, condlist, pos, val;
372
373    func:= arg[1];
374    if Length( arg ) = 2 then
375      condlist:= arg[2];
376    else
377      detect:= arg[2];
378      condlist:= arg[3];
379    fi;
380    pos:= Position( condlist, func );
381    while pos <> fail do
382      if Length( arg ) = 2 then
383        # Support `IsPermGroup' etc. *without* subsequent `true'.
384        Unbind( condlist[ pos ] );
385      else
386        if pos = Length( condlist ) then
387          # Keep `condlist' unchanged.
388          # If there is a call without <detect> then it will remove the entry.
389          return true;
390        fi;
391        val:= condlist[ pos+1 ];
392        if    ( IsString( val ) and detect( val ) )
393           or ( not IsList( val ) and detect( val ) )
394           or ( IsList( val ) and ForAny( val, detect ) ) then
395          Unbind( condlist[ pos ] );
396          Unbind( condlist[ pos+1 ] );
397        fi;
398      fi;
399      pos:= Position( condlist, func, pos );
400    od;
401    return true;
402    end;
403
404
405#############################################################################
406##
407#F  AGR.DeclareDataType( <kind>, <name>, <record> )
408##
409##  Check that the necessary components are bound,
410##  and add default values if necessary.
411##
412AGR.DeclareDataType:= function( kind, name, record )
413    local types, nam;
414
415    # Check that the type does not yet exist.
416    types:= AtlasOfGroupRepresentationsInfo.TableOfContents.types;
417    if ForAny( types.( kind ), x -> x[1] = name ) then
418      Error( "data type <name> exists already" );
419    fi;
420    record:= ShallowCopy( record );
421
422    # Check mandatory components.
423    for nam in [ "FilenameFormat", "AddFileInfo",
424                 "ReadAndInterpretDefault" ] do
425      if not IsBound( record.( nam ) ) then
426        Error( "the component `", nam, "' must be bound in <record>" );
427      fi;
428    od;
429
430    # Add default components.
431    if not IsBound( record.DisplayOverviewInfo ) then
432      record.DisplayOverviewInfo:= fail;
433    fi;
434    if not IsBound( record.TOCEntryString ) then
435      record.TOCEntryString := TOCEntryStringDefault;
436    fi;
437    if not IsBound( record.PostprocessFileInfo ) then
438      record.PostprocessFileInfo := Ignore;
439    fi;
440    if   kind = "rep" then
441      for nam in [ "DisplayGroup", "AddDescribingComponents" ] do
442        if not IsBound( record.( nam ) ) then
443          Error( "the component `", nam, "' must be bound in <record>" );
444        fi;
445      od;
446      if not IsBound( record.AccessGroupCondition ) then
447        record.AccessGroupCondition := ReturnFalse;
448      fi;
449      if not IsBound( record.TestFileHeaders ) then
450        record.TestFileHeaders := ReturnTrue;
451      fi;
452      if not IsBound( record.TestFiles ) then
453        record.TestFiles := ReturnTrue;
454      fi;
455    elif kind = "prg" then
456      if not IsBound( record.DisplayPRG ) then
457        record.DisplayPRG := function( tocs, names, std, stdavail )
458            return []; end;
459      fi;
460      if not IsBound( record.AccessPRG ) then
461        record.AccessPRG := function( toc, groupname, std, conditions )
462          return fail;
463        end;
464      fi;
465      if not IsBound( record.AtlasProgram ) then
466        record.AtlasProgram := AtlasProgramDefault;
467      fi;
468      if not IsBound( record.AtlasProgramInfo ) then
469        record.AtlasProgramInfo := AtlasProgramInfoDefault;
470      fi;
471    else
472      Error( "<kind> must be one of \"rep\", \"prg\"" );
473    fi;
474
475    # Add the pair.
476    Add( types.( kind ), [ name, record, kind ] );
477
478    # Clear the cache.
479    types.cache:= [];
480    end;
481
482
483#############################################################################
484##
485#F  AGR.DataTypes( <kind1>[, <kind2>] )
486##
487##  returns the list of pairs <C>[ <A>name</A>, <A>record</A> ]</C>
488##  as declared for the kinds in question.
489##
490AGR.DataTypes:= function( arg )
491    local types, result, kind;
492
493    types:= AtlasOfGroupRepresentationsInfo.TableOfContents.types;
494    result:= First( types.cache, x -> x[1] = arg );
495
496    if result = fail then
497      result:= [];
498      for kind in arg do
499        if IsBound( types.( kind ) ) then
500          Append( result, types.( kind ) );
501        fi;
502      od;
503      result:= [ arg, result ];
504      Add( types.cache, result );
505    fi;
506
507    return result[2];
508    end;
509
510
511#############################################################################
512##
513#F  AGR.VersionOfSLP( <filename> )
514##
515##  Note that 'cyc2ccl' scripts involve *two* version numbers,
516##  the one of the corresponding 'cyc' script and the one of their own.
517##  We return the *list* of these version numbers in this case.
518##
519AGR.VersionOfSLP:= function( filename )
520    local len, pos, pos2;
521
522    if not IsString( filename ) then
523      # a list of one or more file descriptions, take the first of them
524      filename:= filename[1];
525      if not IsString( filename ) then
526        # a file from a private extension
527        filename:= filename[2];
528      fi;
529    fi;
530
531    len:= Length( filename );
532    pos:= len;
533    while IsDigitChar( filename[ pos ] ) do
534      pos:= pos - 1;
535    od;
536    if 6 < pos and filename{ [ pos-5 .. pos ] } = "-cclsW" then
537      # Check whether we are in the case of a 'cyc2ccls' script.
538      pos2:= pos-6;
539      while IsDigitChar( filename[ pos2 ] ) do
540        pos2:= pos2 - 1;
541      od;
542      if 4 < pos2 and filename{ [ pos2-3 .. pos2 ] } = "cycW" then
543        return [ filename{ [ pos2+1 .. pos-6 ] },
544                 filename{ [ pos+1 .. len ] } ];
545      fi;
546    fi;
547
548    return filename{ [ pos+1 .. len ] };
549    end;
550
551
552#############################################################################
553##
554#F  AGR.StandardizeMaximalSubgroup( <groupname>, <maxslpname>, <std>, <vers> )
555##
556##  returns <K>fail</K> or the list
557##  <C>[ <A>filename</A>, <A>std</A> ]</C>
558##  where a straight line program for standardizing the generators of the
559##  maximal subgroup given by <maxslpname> can be found in the file
560##  <A>filename</A> (which is either a string or a list containng the
561##  name of the table of contents and the actual filename.
562##  The standardization number of the result is <A>std</A>.
563##  One can prescribe this standardization via a list or integer <std>,
564##  otherwise one can enter <K>true</K> as <std>.
565##  If <A>vers</A> is <K>true</K> then any version of the program is taken,
566##  otherwise only a version in the list <A>vers</A>.
567##
568AGR.StandardizeMaximalSubgroup:= function( groupname, maxslpname, std, vers )
569    local prefix, toc, r, l, filename;
570
571    if IsInt( std ) then
572      std:= [ std ];
573    fi;
574    if IsInt( vers ) then
575      vers:= [ vers ];
576    fi;
577    prefix:= ReplacedString( maxslpname, "-", "" );
578    for toc in AGR.TablesOfContents( "all" ) do
579      if IsBound( toc.( groupname ) ) then
580        r:= toc.( groupname );
581        if IsBound( r.maxstd ) then
582          for l in r.maxstd do
583            filename:= l[6];
584            if l[6]{ [ 1 .. Position( filename, '-' ) - 1 ] } = prefix and
585               ( std = true or l[5] in std ) and
586               ( vers = true or AGR.VersionOfSLP( filename ) in vers ) then
587              if toc.TocID <> "core" then
588                return [ [ toc.TocID, filename], l[5] ];
589              else
590                return [ filename, l[5] ];
591              fi;
592            fi;
593          od;
594        fi;
595      fi;
596    od;
597
598    return fail;
599    end;
600
601
602#############################################################################
603##
604#F  AGR.CommonDisplayPRG( <title>, <stdavail>, <data>, <onelineonly> )
605##
606##  This is a utility for the 'DisplayPRG' functions of "prg" types.
607##
608##  <data> is a matrix whose columns correspond to
609##  1. the values to be shown in the first column
610##  2. the privacy flags
611##  3. the standardizations,
612##  4. the versions,
613##  5. the identifiers,
614##  6. (optional) additional information for the second column
615##
616AGR.CommonDisplayPRG:= function( title, stdavail, data, onelineonly )
617    local res, entry, line, private;
618
619    res:= [];
620
621    if Length( data ) = 0 then
622      return res;
623    elif Length( data ) = 1 and onelineonly then
624      # Show just one line.
625      # (If there is only one line then showing version info makes no sense.)
626      entry:= data[1];
627      if 1 < Length( stdavail ) then
628        Add( res, Concatenation( "(for std. generators ", entry[3], ")" ) );
629      fi;
630      if IsBound( entry[6] ) then
631        Add( res, Concatenation( "(", entry[6], ")" ) );
632      fi;
633      return [ Concatenation( title, entry[2] ),
634               JoinStringsWithSeparator( res, "  " ),
635               entry[5] ];
636    else
637      # Show a header line plus one line for each program.
638      for entry in data do
639        line:= [];
640        # Show the standardization only if several are available.
641        if 1 < Length( stdavail ) then
642          Add( line, Concatenation( "(for std. gen. ", entry[3], ")" ) );
643        fi;
644        # Show the version only if several are available.
645        if 0 < Number( data, x -> x[1] = entry[1] and x[4] <> entry[4] ) then
646          Add( line, Concatenation( "(version ", entry[4], ")" ) );
647        fi;
648        # Show additional information whenever available.
649        if IsBound( entry[6] ) then
650          Add( line, Concatenation( "(", entry[6], ")" ) );
651        fi;
652        Add( res, [ Concatenation( entry[1], entry[2] ),
653                    JoinStringsWithSeparator( line, "  " ),
654                    entry[5] ] );
655      od;
656
657      private:= First( data, x -> x[2] <> "" );
658      if private = fail then
659        private:= "";
660      else
661        private:= private[2];
662      fi;
663      return Concatenation( [ Concatenation( title, private ) ], res );
664    fi;
665    end;
666
667
668#############################################################################
669##
670#F  AtlasProgramInfoForFilename( <filename> )
671##
672##  returns the `AtlasProgramInfo' record for the straight line program with
673##  filename <filename>, which must be a string.
674##
675##  A copy of this function is in 'ctblocks/gap/access.g'!
676##
677BindGlobal( "AtlasProgramInfoForFilename", function( filename )
678    local type, parsed, gapname, toc, data, l, id;
679
680    for type in AGR.DataTypes( "prg" ) do
681      parsed:= AGR.ParseFilenameFormat( filename, type[2].FilenameFormat );
682      if parsed <> fail then
683        # We need always GAP name, filename, and standardization.
684        # In case of a private extension,
685        # the second entry is a pair of the form [ <tocid>, <gapname> ].
686        gapname:= First( AtlasOfGroupRepresentationsInfo.GAPnames,
687                         pair -> pair[2] = parsed[1] )[1];
688
689        for toc in AGR.TablesOfContents( "all" ) do
690          if IsBound( toc.( parsed[1] ) ) then
691            data:= toc.( parsed[1] );
692            if IsBound( data.( type[1] ) ) then
693              for l in data.( type[1] ) do
694                if l[ Length( l ) ] = filename then
695                  id:= [ gapname, filename, parsed[3] ];
696                  if toc.TocID <> "core" then
697                    id[2]:= [ [ toc.TocID, filename ] ];
698                  fi;
699                  # ...
700                  if type[1] in [ "check", "find", "kernel", "pres", "switch" ] then
701                    id[4]:= parsed[5];
702                  elif type[1] = "out" then
703                    id[3]:= parsed[5];
704#T is this really intended? -> better add the standardization of G!!!
705                  elif not type[1] in [ "maxes", "classes", "cyclic",
706                                        "cyc2ccl", "maxstd", "switch",
707                                        "otherscripts" ] then
708                    Error( "unknown type <type>" );
709                  fi;
710                  return AtlasProgramInfo( id );
711                fi;
712              od;
713            fi;
714          fi;
715        od;
716      fi;
717    od;
718
719    return fail;
720    end );
721
722
723#############################################################################
724##
725#E
726
727