Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
| Download
GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
Project: cocalc-sagemath-dev-slelievre
Views: 418346############################################################################# #### ## #A anupq.gi ANUPQ package Eamonn O'Brien #A & Frank Celler ## #Y Copyright 1992-1994, Lehrstuhl D fuer Mathematik, RWTH Aachen, Germany #Y Copyright 1992-1994, School of Mathematical Sciences, ANU, Australia ## ############################################################################# ## #F ANUPQDirectoryTemporary( <dir> ) . . . . . redefine ANUPQ temp directory ## ## calls the UNIX command `mkdir' to create <dir>, which must be a string, ## and if successful a directory object for <dir> is both assigned to ## `ANUPQData.tmpdir' and returned. The field `ANUPQData.outfile' is also ## set to be a file in `ANUPQData.tmpdir', and on exit from {\GAP} <dir> is ## removed. ## InstallGlobalFunction(ANUPQDirectoryTemporary, function(dir) local created; # check arguments if not IsString(dir) then Error( "usage: ANUPQDirectoryTemporary( <dir> ) : <dir> must be a string.\n"); fi; # create temporary directory created := Process(DirectoryCurrent(), Filename( DirectoriesSystemPrograms(), "sh" ), InputTextUser(), OutputTextUser(), [ "-c", Concatenation("mkdir ", dir) ]); if created = fail then return fail; fi; Add( GAPInfo.DirectoriesTemporary, dir ); ANUPQData.tmpdir := Directory(dir); ANUPQData.outfile := Filename(ANUPQData.tmpdir, "PQ_OUTPUT"); return ANUPQData.tmpdir; end); ############################################################################# ## #F ANUPQerrorPq( <param> ) . . . . . . . . . . . . . . . . . report an error ## InstallGlobalFunction( ANUPQerrorPq, function( param ) Error( "Valid Options:\n", " \"ClassBound\", <bound>\n", " \"Prime\", <prime>\n", " \"Exponent\", <exponent>\n", " \"Metabelian\"\n", " \"OutputLevel\", <level>\n", " \"Verbose\"\n", " \"SetupFile\", <file>\n", " \"PqWorkspace\", <workspace>\n", "Illegal Parameter: \"", param, "\"" ); end ); ############################################################################# ## #F ANUPQextractPqArgs( <args> ) . . . . . . . . . . . . . extract arguments ## InstallGlobalFunction( ANUPQextractPqArgs, function( args ) local CR, i, act, match; # allow to give only a prefix match := function( g, w ) return 1 < Length(g) and Length(g) <= Length(w) and w{[1..Length(g)]} = g; end; # extract arguments CR := rec(); i := 2; while i <= Length(args) do act := args[i]; if not IsString( act ) then ANUPQerrorPq( act ); fi; # "ClassBound", <class> if match( act, "ClassBound" ) then i := i + 1; CR.ClassBound := args[i]; # "Prime", <prime> elif match( act, "Prime" ) then i := i + 1; CR.Prime := args[i]; # "Exponent", <exp> elif match( act, "Exponent" ) then i := i + 1; CR.Exponent := args[i]; # "Metabelian" elif match( act, "Metabelian" ) then CR.Metabelian := true; # "Output", <level> elif match( act, "OutputLevel" ) then i := i + 1; CR.OutputLevel := args[i]; CR.Verbose := true; # "SetupFile", <file> elif match( act, "SetupFile" ) then i := i + 1; CR.SetupFile := args[i]; # "PqWorkspace", <workspace> elif match( act, "PqWorkspace" ) then i := i + 1; CR.PqWorkspace := args[i]; # "Verbose" elif match( act, "Verbose" ) then CR.Verbose := true; # signal an error else ANUPQerrorPq( act ); fi; i := i + 1; od; return CR; end ); ############################################################################# ## #V ANUPQGlobalVariables ## InstallValue( ANUPQGlobalVariables, [ "F", # a free group "MapImages" # images of the generators in G ] ); ############################################################################# ## #F ANUPQReadOutput . . . . read pq output without affecting global variables ## InstallGlobalFunction( ANUPQReadOutput, function( file, globalvars ) local var, result; for var in globalvars do HideGlobalVariables( var ); od; Read( file ); result := rec(); for var in globalvars do if IsBoundGlobal( var ) then result.(var) := ValueGlobal( var ); else result.(var) := fail; fi; od; for var in globalvars do UnhideGlobalVariables( var ); od; return result; end ); ############################################################################# ## #F PqEpimorphism( <arg> : <options> ) . . . . . epimorphism onto p-quotient ## InstallGlobalFunction( PqEpimorphism, function( arg ) return PQ_EPI_OR_PCOVER(arg : PqEpiOrPCover := "pQepi"); end ); ############################################################################# ## #F Pq( <arg> : <options> ) . . . . . . . . . . . . . . . . . . . p-quotient ## InstallGlobalFunction( Pq, function( arg ) return PQ_EPI_OR_PCOVER(arg : PqEpiOrPCover := "pQuotient"); end ); ############################################################################# ## #F PqPCover( <arg> : <options> ) . . . . . . p-covering group of p-quotient ## InstallGlobalFunction( PqPCover, function( arg ) return PQ_EPI_OR_PCOVER(arg : PqEpiOrPCover := "pCover"); end ); ############################################################################# ## #F PQ_GROUP_FROM_PCP(<datarec>,<out>) . extract gp from pq pcp file into GAP ## InstallGlobalFunction( PQ_GROUP_FROM_PCP, function( datarec, out ) HideGlobalVariables( "F", "MapImages" ); Read( datarec.outfname ); if out = "pCover" then datarec.pCover := ValueGlobal( "F" ); IsPGroup( datarec.pCover ); else datarec.pQepi := GroupHomomorphismByImagesNC( datarec.group, ValueGlobal( "F" ), GeneratorsOfGroup( datarec.group ), ValueGlobal( "MapImages" ) ); SetIsSurjective( datarec.pQepi, true ); datarec.pQuotient := Image( datarec.pQepi ); IsPGroup( datarec.pQuotient ); fi; UnhideGlobalVariables( "F", "MapImages" ); end ); ############################################################################# ## #F TRIVIAL_PQ_GROUP(<datarec>, <out>) . . . extract gp when trivial into GAP ## InstallGlobalFunction( TRIVIAL_PQ_GROUP, function( datarec, out ) local Q; Q := TrivialGroup( IsPcGroup ); if out = "pCover" then datarec.pCover := Q; IsPGroup( datarec.pCover ); else datarec.pQepi := GroupHomomorphismByFunction( datarec.group, Q, g -> One(Q) ); SetIsSurjective( datarec.pQepi, true ); datarec.pQuotient := Image( datarec.pQepi ); IsPGroup( datarec.pQuotient ); fi; end ); ############################################################################# ## #F PQ_EPI_OR_PCOVER(<args>:<options>) . p-quotient, its epi. or its p-cover ## InstallGlobalFunction( PQ_EPI_OR_PCOVER, function( args ) local out, datarec, AtClass, trivial; out := ValueOption("PqEpiOrPCover"); datarec := ANUPQ_ARG_CHK("Pq", args); datarec.filter := ["Output file in", "Group presentation"]; VALUE_PQ_OPTION("Identities", [], datarec); if datarec.calltype = "GAP3compatible" then # ANUPQ_ARG_CHK calls PQ_EPI_OR_PCOVER itself in this case # (so datarec.(out) has already been computed) return datarec.(out); fi; trivial := IsEmpty( datarec.group!.GeneratorsOfMagmaWithInverses ); if trivial then ; #the `pq' binary spits out nonsense if given a trivial pres'n elif datarec.calltype = "interactive" and ( IsBound(datarec.pQuotient) or IsBound(datarec.pCover) ) then AtClass := function() return IsBound(datarec.complete) and datarec.complete or IsBound(datarec.class) and datarec.class = datarec.ClassBound; end; if IsBound(datarec.pcoverclass) and datarec.pcoverclass = datarec.class and not AtClass() then # ``reduce'' the p-cover to a p-class PQ_FINISH_NEXT_CLASS( datarec ); fi; while not AtClass() do PQ_NEXT_CLASS( datarec ); od; # the following is not executed if the while-loop is # executed at least once if IsBound( datarec.(out) ) then return datarec.(out); # it had already been computed fi; else PQ_PC_PRESENTATION(datarec, "pQ"); if datarec.class < Minimum(63, datarec.ClassBound) then datarec.complete := true; fi; fi; trivial := trivial or IsEmpty(datarec.ngens) or datarec.ngens[1] = 0; if not trivial then if out = "pCover" then PQ_P_COVER( datarec ); fi; PushOptions( rec(nonuser := true) ); PQ_WRITE_PC_PRESENTATION(datarec, datarec.outfname); PopOptions(); fi; if datarec.calltype = "non-interactive" then PQ_COMPLETE_NONINTERACTIVE_FUNC_CALL(datarec); if IsBound( datarec.setupfile ) then if trivial then return fail; fi; return true; fi; fi; if trivial then TRIVIAL_PQ_GROUP( datarec, out ); else # read group and images from file PQ_GROUP_FROM_PCP( datarec, out ); fi; return datarec.(out); end ); ############################################################################# ## #F PqRecoverDefinitions( <G> ) . . . . . . . . . . . . . . . . . definitions ## ## This function finds a definition for each generator of the p-group <G>. ## These definitions need not be the same as the ones used by pq. But ## they serve the purpose of defining each generator as a commutator or ## power of earlier ones. This is useful for extending an automorphism that ## is given on a set of minimal generators of <G>. ## InstallGlobalFunction( PqRecoverDefinitions, function( G ) local col, gens, definitions, h, g, rhs, gen; col := ElementsFamily( FamilyObj( G ) )!.rewritingSystem; gens := GeneratorsOfRws( col ); definitions := []; for h in [1..NumberGeneratorsOfRws( col )] do rhs := GetPowerNC( col, h ); if Length( rhs ) = 1 then gen := Position( gens, rhs ); if not IsBound( definitions[gen] ) then definitions[gen] := h; fi; fi; for g in [1..h-1] do rhs := GetConjugateNC( col, h, g ); if Length( rhs ) = 2 then gen := SubSyllables( rhs, 2, 2 ); gen := Position( gens, gen ); if not IsBound( definitions[gen] ) then definitions[gen] := [h, g]; fi; fi; od; od; return definitions; end ); ############################################################################# ## #F PqAutomorphism( <epi>, <autoimages> ) . . . . . . . . . . . . definitions ## ## Take an automorphism of the preimage and produce the induced automorphism ## of the image of the epimorphism. ## InstallGlobalFunction( PqAutomorphism, function( epi, autoimages ) local G, p, gens, definitions, d, epimages, i, pos, def, phi; G := Image( epi ); p := PrimePGroup( G ); gens := GeneratorsOfGroup( G ); autoimages := List( autoimages, im->Image( epi, im ) ); ## Get a definition for each generator. definitions := PqRecoverDefinitions( G ); d := Number( [1..Length(definitions)], i->not IsBound( definitions[i] ) ); ## Find the images for the defining generators of G under the ## automorphism. We have to be careful, as some of the generators for ## the source might be redundant as generators of G. epimages := List( GeneratorsOfGroup(Source(epi)), g->Image(epi,g) ); for i in [1..d] do ## Find G.i ... pos := Position( epimages, G.(i) ); if pos = fail then Error( "generators ", i, "not image of a generators" ); fi; ## ... and set its image. definitions[i] := autoimages[pos]; od; ## Replace each definition by its image under the automorphism. for i in [d+1..Length(definitions)] do def := definitions[i]; if IsInt( def ) then definitions[i] := definitions[ def ]^p; else definitions[i] := Comm( definitions[ def[1] ], definitions[ def[2] ] ); fi; od; phi := GroupHomomorphismByImages( G, G, gens, definitions ); SetIsBijective( phi, true ); return phi; end ); ############################################################################# ## #F PqLeftNormComm( <words> ) . . . . . . . . . . . . . left norm commutator ## ## returns for a list <words> of words in the generators of a group the left ## norm commutator of <words>, e.g.~if <w1>, <w2>, <w3> are words in the ## generators of some free or fp group then `PqLeftNormComm( [<w1>, <w2>, ## <w3>] );' is equivalent to `Comm( Comm( <w1>, <w2> ), <w3> );'. Actually, ## the only restrictions on <words> are that <words> must constitute a list ## of group elements of the same group (so a list of permutations is ## allowed, for example) and that <words> must contain at least *two* words. ## InstallGlobalFunction( PqLeftNormComm, function( words ) local fam, comm, word; if not IsList(words) or 2 > Length(words) or not ForAll(words, IsMultiplicativeElementWithInverse) then Error( "<words> should be a list of at least 2 group elements\n" ); else fam := FamilyObj(words[1]); if not ForAll(words, w -> IsIdenticalObj(FamilyObj(w), fam)) then Error( "<words> should belong to the same group\n" ); fi; fi; comm := words[1]; for word in words{[2 .. Length(words)]} do comm := Comm(comm, word); od; return comm; end ); ############################################################################# ## #F PqGAPRelators( <group>, <rels> ) . . . . . . . . pq relators as GAP words ## ## returns a list of words that {\GAP} understands, given a list <rels> of ## strings in the string representations of the generators of the fp group ## <group> prepared as a list of relators for the `pq' program. ## ## *Note:* ## The `pq' program does not use `/' to indicate multiplication by an ## inverse and uses square brackets to represent (left normed) commutators. ## Also, even though the `pq' program accepts relations, all elements of ## <rels> *must* be in relator form, i.e.~a relation of form `<w1> = <w2>' ## must be written as `<w1>*(<w2>)^-1'. ## ## Here is an example: ## ## \beginexample ## gap> F := FreeGroup("a", "b"); ## gap> PqGAPRelators(F, [ "a*b^2", "[a,b]^2*a", "([a,b,a,b,b]*a*b)^2*a" ]); ## [ a*b^2, a^-1*b^-1*a*b*a^-1*b^-1*a*b*a, b^-1*a^-1*b^-1*a^-1*b*a*b^-1*a*b*a^ ## -1*b*a^-1*b^-1*a*b*a*b^-1*a^-1*b^-1*a^-1*b*a*b^-1*a*b^-1*a^-1*b*a^-1*b^ ## -1*a*b*a*b*a^-1*b*a*b^-1*a*b*a^-1*b*a^-1*b^-1*a*b*a*b^-1*a^-1*b^-1*a^ ## -1*b*a*b^-1*a*b^-1*a^-1*b*a^-1*b^-1*a*b*a*b^2*a*b*a ] ## \endexample ## InstallGlobalFunction( PqGAPRelators, function( group, rels ) local gens, relgens, diff, g; if not( IsFpGroup(group) ) then Error("<group> must be an fp group\n"); fi; gens := List( FreeGeneratorsOfFpGroup(group), String ); if not ForAll(rels, rel -> Position(rel, '/') = fail) then Error( "pq binary does not understand `/' in relators\n" ); fi; relgens := Set( Concatenation( List( rels, rel -> Filtered( SplitString(rel, "", "*[]()^, "), str -> Int(str) = fail) ) ) ); diff := Difference(relgens, gens); if not IsEmpty(diff) then Error( "generators: ", diff, "\nare not among the generators of the group supplied\n" ); fi; CallFuncList(HideGlobalVariables, gens); for g in FreeGeneratorsOfFpGroup(group) do ASS_GVAR(String(g), g); od; rels := List( rels, rel -> EvalString( ReplacedString( ReplacedString(rel, "]", "])"), "[", "PqLeftNormComm([" ) ) ); CallFuncList(UnhideGlobalVariables, gens); return rels; end ); ############################################################################# ## #F PqParseWord( <F>, <word> ) . . . . . . . . . . . . parse word through GAP #F PqParseWord( <n>, <word> ) ## ## parse <word> through {\GAP}, where <word> is a string representing a word ## in the generators of <F> (the first form of `PqParseWord') or <n> pc ## generators `x1,...,x<n>'. `PqParseWord' is provided as a rough-and-ready ## check of <word> for syntax errors. A syntax error will cause the entering ## of a `break'-loop, in which the error message may or may not be ## meaningful (depending on whether the syntax error gets caught at the ## {\GAP} or kernel level). ## ## *Note:* ## The reason the generators *must* be `x1,...,x<n>' in the second form of ## `PqParseWord' is that these are the pc generator names used by the `pq' ## program (as distinct from the generator names for the group provided by ## the user to a function like `Pq' that invokes the `pq' program). ## InstallGlobalFunction( PqParseWord, function( n, word ) local ParseOnBreak, ParseOnBreakMessage, NormalOnBreak, NormalOnBreakMessage, parts, gens; if IsGroup(n) or Position(word, '[') <> fail or Position(word, '(') <> fail then #pass word through GAP's parser to see if it's ok NormalOnBreak := OnBreak; ParseOnBreak := function() Where(0); OnBreak := NormalOnBreak; end; OnBreak := ParseOnBreak; if IsFunction(OnBreakMessage) then NormalOnBreakMessage := OnBreakMessage; ParseOnBreakMessage := function() Print( " syntax error in: ", word, "\n" ); Print( " you can type: 'quit;' to quit to outer loop.\n" ); OnBreakMessage := NormalOnBreakMessage; end; OnBreakMessage := ParseOnBreakMessage; fi; if IsGroup(n) then PqGAPRelators(n, [ word ]); else PqGAPRelators(FreeGroup(n, "x"), [ word ]); fi; OnBreak := NormalOnBreak; if IsFunction(OnBreakMessage) then OnBreakMessage := NormalOnBreakMessage; fi; else parts := List( SplitString(word, "*"), part -> SplitString(part, "^") ); if ForAny( parts, part -> 2 < Length(part) or 2 = Length(part) and not IsInt( Int(part[2]) ) ) then Error( "detected invalid exponent in argument <word>: ", word, "\n"); fi; if ForAny( parts, part -> IsEmpty( part[1] ) or part[1][1] <> 'x' ) then Error( "generators in argument <word> must all be of form:\n", "`x<i>' for some integer <i>\n" ); fi; gens := List( parts, part -> Int( part[1]{[2 .. Length(part[1])]} ) ); if not ForAll(gens, gen -> IsPosInt(gen) and gen <= n) then Error( "generators in argument <word> must be in the range: ", "x1,...,x", n, "\n" ); fi; fi; return true; end ); ############################################################################# ## #F PQ_EVALUATE( <string> ) . . . . . . . . . evaluate a string emulating GAP ## ## For each substring of the string <string> that is a statement (i.e. ends ## in a `;'), `PQ_EVALUATE( <string> )' evaluates it in the same way {\GAP} ## would. If the substring is further followed by a `;' (i.e. there was ## `;;'), this is an indication that the statement would produce no output; ## otherwise the output that the user would normally see if she typed the ## statement interactively is displayed. ## InstallGlobalFunction(PQ_EVALUATE, function(string) local from, pos, statement, parts, var; from := 0; pos := Position(string, ';', from); while pos <> fail do statement := string{[from + 1..pos]}; statement := ReplacedString(statement," last "," ANUPQData.example.last "); if pos < Length(string) and string[pos + 1] = ';' then Read( InputTextString(statement) ); from := pos + 1; else parts := SplitString(statement, "", " \n"); if 1 < Length(parts) and parts[2] = ":=" then Read( InputTextString(statement) ); Read( InputTextString( Concatenation( "View(", parts[1], "); Print(\"\\n\");" ) ) ); ANUPQData.example.last := parts[1]; else var := TemporaryGlobalVarName(); Read( InputTextString( Concatenation(var, ":=", statement) ) ); if ISBOUND_GLOBAL(var) then View( VALUE_GLOBAL(var) ); Print( "\n" ); ANUPQData.example.last := VALUE_GLOBAL(var); UNBIND_GLOBAL(var); fi; fi; from := pos; fi; pos := Position(string, ';', from); od; end ); ############################################################################# ## #F PqExample() . . . . . . . . . . execute a pq example or display the index #F PqExample( <example>[, PqStart][, Display] ) #F PqExample( <example>[, PqStart][, <filename>] ) ## ## With no arguments, or with single argument `"index"', or a string ## <example> that is not the name of a file in the `examples' directory, an ## index of available examples is displayed. ## ## With just the one argument <example> that is the name of a file in the ## `examples' directory, the example contained in that file is executed in ## its simplest form. Some examples accept options which you may use to ## modify some of the options used in the commands of the example. To find ## out which options an example accepts, use one of the mechanisms for ## displaying the example described below. ## ## Some examples have both non-interactive and interactive forms; those that ## are non-interactive only have a name ending in `-ni'; those that are ## interactive only have a name ending in `-i'; examples with names ending ## in `.g' also have only one form; all other examples have both ## non-interactive and interactive forms and for these giving `PqStart' as ## second argument invokes `PqStart' initially and makes the appropriate ## adjustments so that the example is executed or displayed using ## interactive functions. ## ## If `PqExample' is called with last (second or third) argument `Display' ## then the example is displayed without being executed. If the last ## argument is a non-empty string <filename> then the example is also ## displayed without being executed but is also written to a file with that ## name. Passing an empty string as last argument has the same effect as ## passing `Display'. ## ## *Note:* ## The variables used in `PqExample' are local to the running of ## `PqExample', so there's no danger of having some of your variables ## over-written. However, they are not completely lost either. They are ## saved to a record `ANUPQData.examples.vars', i.e.~if `F' is a variable ## used in the example then you will be able to access it after `PqExample' ## has finished as `ANUPQData.examples.vars.F'. ## InstallGlobalFunction(PqExample, function(arg) local name, file, instream, line, input, doPqStart, vars, var, printonly, filename, DoAltAction, GetNextLine, PrintLine, action, datarec, optname, linewidth, sizescreen, CheckForCompoundKeywords, hasFunctionExpr, parts, iscompoundStatement, compoundDepth; sizescreen := SizeScreen(); if sizescreen[1] < 80 then SizeScreen([80, sizescreen[2]]); linewidth := 80; else linewidth := sizescreen[1]; fi; if IsEmpty(arg) then name := "index"; else name := arg[1]; fi; if name = "README" then file := fail; else file := Filename(DirectoriesPackageLibrary( "anupq", "examples"), name); fi; if file = fail then Info(InfoANUPQ + InfoWarning, 1, "Sorry! There is no ANUPQ example with name `", name, "'", " ... displaying index."); name := "index"; file := Filename(DirectoriesPackageLibrary( "anupq", "examples"), name); fi; if name <> "index" then doPqStart := false; if Length(arg) > 1 then # At this point the name of the variable <printonly> doesn't make # sense; however, if the value assigned to <printonly> is `Display' # or an empty string then we ``print only'' and if it is a non-empty # string then it is assumed to be a filename and we `LogTo' that filename. printonly := arg[Minimum(3, Length(arg))]; if arg[2] = PqStart then if 2 < Length(name) and name{[Length(name) - 1 .. Length(name)]} in ["-i", "ni", ".g"] then Error( "example does not have a (different) interactive form\n" ); fi; doPqStart := true; fi; else printonly := false; fi; DoAltAction := function() if doPqStart then if action[2] = "do" then # uncomment line line := line{[2..Length(line)]}; else # replace a variable with a proc id line := ReplacedString( line, action[5], action[3] ); fi; fi; end; if printonly = Display or IsString(printonly) then GetNextLine := function() local from, to; line := ReadLine(instream); if line = fail then return; elif IsBound(action) then action := SplitString(action, "", "# <>\n"); DoAltAction(); Unbind(action); elif 3 < Length(line) and line{[1..4]} = "#alt" then # only "#alt" actions recognised action := line; elif IsMatchingSublist(line, "#comment:") then line := ReplacedString(line, " supplying", ""); from := Position(line, ' '); to := Position(line, '<', from); Info(InfoANUPQ, 1, "In the next command, you may", line{[from .. to - 1]}); from := to + 1; to := Position(line, '>') - 1; Info(InfoANUPQ, 1, "supplying to `PqExample' the option: `", line{[from .. to]}, "'"); fi; end; if IsString(printonly) and printonly <> "" then filename := printonly; LogTo( filename ); #Make sure it's empty and writable fi; PrintLine := function() if IsMatchingSublist(line, "##") then line := line{[2..Length(line)]}; elif line[1] = '#' then return; fi; Print( ReplacedString(line, ";;", ";") ); end; printonly := true; #now the name of the variable makes sense else printonly := false; ANUPQData.example := rec(options := rec()); datarec := ANUPQData.example.options; CheckForCompoundKeywords := function() local compoundkeywords; compoundkeywords := Filtered( SplitString(line, "", "( ;\n"), w -> w in ["do", "od", "if", "fi", "repeat", "until", "function", "end"] ); hasFunctionExpr := "function" in compoundkeywords; compoundDepth := compoundDepth + Number(compoundkeywords, w -> w in ["do", "if", "repeat", "function"]) - Number(compoundkeywords, w -> w in ["od", "fi", "until", "end"]); return not IsEmpty(compoundkeywords); end; GetNextLine := function() local from, to, bhsinput; repeat line := ReadLine(instream); if line = fail then return; fi; until not IsMatchingSublist(line, "#comment:"); if IsBound(action) then action := SplitString(action, "", "# <>\n"); if action[1] = "alt:" then DoAltAction(); else # action[2] = name of a possible option passed to `PqExample' # action[4] = string to be replaced in <line> with the value # of the option if ok and set optname := action[2]; if IsDigitChar(optname[ Length(optname) ]) then optname := optname{[1..Length(optname) - 1]}; fi; datarec.(action[2]) := ValueOption(action[2]); if datarec.(action[2]) = fail then Unbind( datarec.(action[2]) ); else if not ANUPQoptionChecks.(optname)( datarec.(action[2]) ) then Info(InfoANUPQ, 1, "\"", action[2], "\" value must be a ", ANUPQoptionTypes.(optname), ": option ignored."); Unbind( datarec.(action[2]) ); else if action[1] = "add" then line[1] := ' '; fi; if IsString( datarec.(action[2]) ) then line := ReplacedString( line, action[4], Flat(['"',datarec.(action[2]),'"']) ); else line := ReplacedString( line, action[4], String( datarec.(action[2]) ) ); fi; fi; fi; fi; Unbind(action); elif IsMatchingSublist(line, "##") then ; # do nothing elif 3 < Length(line) and line{[1..4]} in ["#sub", "#add", "#alt"] then action := line; elif line[1] = '#' then # execute instructions behind the scenes bhsinput := ""; repeat Append( bhsinput, ReplacedString(line{[2..Length(line)]}, "datarec", "ANUPQData.example.options") ); line := ReadLine(instream); until line[1] <> '#' or (3 < Length(line) and line{[1..4]} in ["#sub", "#add", "#com"]); Read( InputTextString(bhsinput) ); fi; end; PrintLine := function() if IsMatchingSublist(line, "##") then line := line{[2..Length(line)]}; elif line[1] = '#' then return; fi; if input = "" then Print("gap> "); else Print("> "); fi; Print( ReplacedString(line, ";;", ";") ); end; fi; fi; instream := InputTextFile(file); if name <> "index" then FLUSH_PQ_STREAM_UNTIL( instream, 10, 1, ReadLine, line -> IsMatchingSublist(line, "#Example") ); line := FLUSH_PQ_STREAM_UNTIL( instream, 1, 10, ReadLine, line -> IsMatchingSublist(line, "#vars:") ); if Length(line) + 21 < linewidth then Info(InfoANUPQ, 1, line{[Position(line, ' ')+1..Position(line, ';')-1]}, " are local to `PqExample'"); else #this assumes one has been careful to ensure the `#vars:' line is not #longer than 72 characters. Info(InfoANUPQ, 1, line{[Position(line, ' ')+1..Position(line, ';')-1]}, " are"); Info(InfoANUPQ, 1, "local to `PqExample'"); fi; vars := SplitString(line, "", " ,;\n"); vars := vars{[2 .. Length(vars)]}; if not printonly then CallFuncList(HideGlobalVariables, vars); fi; line := FLUSH_PQ_STREAM_UNTIL(instream, 1, 10, ReadLine, line -> IsMatchingSublist(line, "#options:")); input := ""; GetNextLine(); while line <> fail do PrintLine(); if line[1] <> '#' then if not printonly then if input = "" then compoundDepth := 0; iscompoundStatement := CheckForCompoundKeywords(); elif iscompoundStatement and compoundDepth > 0 then CheckForCompoundKeywords(); fi; if line <> "\n" then Append(input, line); if iscompoundStatement then if compoundDepth = 0 and Position(input, ';') <> fail then Read( InputTextString(input) ); if hasFunctionExpr then parts := SplitString(input, "", ":= \n"); Read( InputTextString( Concatenation( "View(", parts[1], "); Print(\"\\n\");" ) ) ); ANUPQData.example.last := parts[1]; fi; iscompoundStatement := false; input := ""; fi; elif Position(input, ';') <> fail then PQ_EVALUATE(input); input := ""; fi; fi; fi; fi; GetNextLine(); od; if printonly then if IsBound(filename) then LogTo(); fi; else ANUPQData.example.vars := rec(); for var in Filtered(vars, ISBOUND_GLOBAL) do ANUPQData.example.vars.(var) := VALUE_GLOBAL(var); od; Info(InfoANUPQ, 1, "Variables used in `PqExample' are saved ", "in `ANUPQData.example.vars'."); CallFuncList(UnhideGlobalVariables, vars); fi; else FLUSH_PQ_STREAM_UNTIL(instream, 1, 10, ReadLine, line -> line = fail); fi; CloseStream(instream); if linewidth <> sizescreen[1] then SizeScreen( sizescreen ); # restore what was there before fi; end); ############################################################################# ## #F AllPqExamples() . . . . . . . . . . list the names of all ANUPQ examples ## InstallGlobalFunction( AllPqExamples, function() local dir, files; dir := DirectoriesPackageLibrary( "anupq", "examples" )[1]; files := DirectoryContents( Filename( dir, "" )); # Remove certain files files := Difference( files, [".", "..", "index", "README", "CVS", "5gp-PG-e5-i", "7gp-a-x-Rel-i"] ); # Remove files ending with a tilde files := Filtered( files, file -> file[ Length(file) ] <> '~' ); return files; end ); ############################################################################# ## #F GrepPqExamples( <string> ) . . . . . . . grep ANUPQ examples for a string ## ## runs the UNIX command `grep <string>' over the {\ANUPQ} examples and ## returns the list of examples for which there is a match. The actual ## matches are `Info'-ed at `InfoANUPQ' level 2. ## InstallGlobalFunction( GrepPqExamples, function( string ) local dir, str, grep, out, opts, lines, matches, line; dir := DirectoriesPackageLibrary( "anupq", "examples" )[1]; grep := Filename( DirectoriesSystemPrograms(), "grep" ); str := ""; out := OutputTextString( str, true ); opts := Concatenation( [ string ], AllPqExamples() ); Process( dir, grep, InputTextNone(), out, opts ); CloseStream( out ); lines := SplitString( str, "", "\n" ); matches := []; for line in lines do Info(InfoANUPQ, 2, line); Add( matches, SplitString(line, "", ":")[1] ); od; return Set(matches); end ); #E anupq.gi . . . . . . . . . . . . . . . . . . . . . . . . . . . ends here