1#############################################################################
2##
3##  This file is part of GAP, a system for computational discrete algebra.
4##  This file's authors include Alexander Hulpke.
5##
6##  Copyright of GAP belongs to its developers, whose names are too numerous
7##  to list here. Please refer to the COPYRIGHT file for details.
8##
9##  SPDX-License-Identifier: GPL-2.0-or-later
10##
11##  This file allows some fancy accesses to the method selection
12##
13
14#############################################################################
15##
16#F  Print_Value(<val>)
17##
18##  <ManSection>
19##  <Func Name="Print_Value" Arg='val'/>
20##
21##  <Description>
22##  print a number factorized by SUM_FLAGS
23##  </Description>
24##  </ManSection>
25##
26BindGlobal("Print_Value_SFF",function(val)
27  if val>SUM_FLAGS then
28    Print(QuoInt(val,SUM_FLAGS),"*SUM_FLAGS");
29    val:=val mod SUM_FLAGS;
30    if val>0 then
31      Print("+",val);
32    fi;
33  else
34    Print(val);
35  fi;
36end);
37
38#############################################################################
39##
40#F  ApplicableMethod( <opr>, <args>[, <printlevel>[, <nr>]] )
41#F  ApplicableMethodTypes( <opr>, <args>[, <printlevel>[, <nr>]] )
42##
43##  <#GAPDoc Label="ApplicableMethod">
44##  <ManSection>
45##  <Func Name="ApplicableMethod" Arg='opr, args[, printlevel[, nr]]'/>
46##  <Func Name="ApplicableMethodTypes" Arg='opr, args[, printlevel[, nr]]'/>
47##
48##  <Description>
49##  Called with two arguments, <Ref Func="ApplicableMethod"/> returns the
50##  method of highest rank that is applicable for the operation <A>opr</A>
51##  with the arguments in the list <A>args</A>.
52##  The default <A>printlevel</A> is <C>0</C>.
53##  If no method is applicable then <K>fail</K> is returned.
54##  <P/>
55##  If a positive integer is given as the fourth argument <A>nr</A> then
56##  <Ref Func="ApplicableMethod"/> returns the <A>nr</A>-th applicable method
57##  for the operation <A>opr</A> with the arguments in the list <A>args</A>,
58##  where the methods are ordered according to descending rank.
59##  If less than <A>nr</A> methods are applicable then <K>fail</K> is
60##  returned.
61##  <P/>
62##  If the fourth argument <A>nr</A> is the string <C>"all"</C> then
63##  <Ref Func="ApplicableMethod"/>
64##  returns a list of all applicable methods for <A>opr</A> with arguments
65##  <A>args</A>, ordered according to descending rank.
66##  <P/>
67##  Depending on the integer value <A>printlevel</A>, additional information is
68##  printed.  Admissible values and their meaning are as follows.
69##  <P/>
70##  <List>
71##  <Mark>0</Mark>
72##  <Item>
73##      no information,
74##  </Item>
75##  <Mark>1</Mark>
76##  <Item>
77##      information about the applicable method,
78##  </Item>
79##  <Mark>2</Mark>
80##  <Item>
81##      also information about the not applicable methods of higher rank,
82##  </Item>
83##  <Mark>3</Mark>
84##  <Item>
85##      also for each not applicable method the first reason why it is not
86##      applicable,
87##  </Item>
88##  <Mark>4</Mark>
89##  <Item>
90##      also for each not applicable method all reasons why it is not
91##      applicable.
92##  </Item>
93##  <Mark>6</Mark>
94##  <Item>
95##      also the function body of the selected method(s)
96##  </Item>
97##  </List>
98##  <P/>
99##  When a method returned by <Ref Func="ApplicableMethod"/> is called then
100##  it returns either the desired result or the string
101##  <C>"TRY_NEXT_METHOD"</C>, which corresponds to a call to
102##  <Ref Func="TryNextMethod"/> in the method and means that
103##  the method selection would call the next applicable method.
104##  <P/>
105##  <E>Note:</E>
106##  The &GAP; kernel provides special treatment for the infix operations
107##  <C>\+</C>, <C>\-</C>, <C>\*</C>, <C>\/</C>, <C>\^</C>, <C>\mod</C> and
108##  <C>\in</C>.
109##  For some kernel objects (notably cyclotomic numbers,
110##  finite field elements and row vectors thereof) it calls kernel methods
111##  circumventing the method selection mechanism.
112##  Therefore for these operations <Ref Func="ApplicableMethod"/> may return
113##  a method which is not the kernel method actually used.
114##  <P/>
115##  The function <Ref Func="ApplicableMethodTypes"/> takes the <E>types</E>
116##  or <E>filters</E> of the arguments as argument (if only filters are given
117##  of course family predicates cannot be tested).
118##  </Description>
119##  </ManSection>
120##  <#/GAPDoc>
121##
122BIND_GLOBAL("ApplicableMethodTypes",function(arg)
123local oper,narg,args,skip,verbos,fams,flags,i,j,methods,flag,flag2,
124      m,nam,val,erg,has,need,isconstructor;
125  if Length(arg)<2 or not IsList(arg[2]) or not IsFunction(arg[1]) then
126    Error("usage: ApplicableMethodTypes(<opr>,<arglist>[,<verbosity>[,<nr>]])");
127  fi;
128  oper:=arg[1];
129  isconstructor:=IS_CONSTRUCTOR(oper);
130  args:=arg[2];
131  if Length(arg)>2 then
132    verbos:=arg[3];
133  else
134    verbos:=0;
135  fi;
136  if Length(arg)>3 then
137    if IsInt( arg[4] ) then
138      skip:=arg[4] - 1;
139    else
140      skip:= -1;
141    fi;
142    erg:=[];
143  else
144    skip:=0;
145  fi;
146  narg:=Length(args);
147
148  # get families and filters
149  flags:=[];
150  fams:=[];
151  for i in args do
152    if IsFilter(i) then
153      Add(flags,FLAGS_FILTER(i));
154      Add(fams,fail);
155    elif IsType(i) then
156      Add(flags,i![2]);
157      Add(fams,i![1]);
158    else
159      Error("wrong kind of argument");
160    fi;
161  od;
162
163  if ForAny(fams,i->i=fail) then
164    fams:=fail;
165    Info(InfoWarning,1,"Family predicate cannot be tested");
166  fi;
167
168  methods:=MethodsOperation(oper,narg);
169  if verbos > 0 then
170    Print("#I  Searching Method for ",NameFunction(oper)," with ",narg,
171	  " arguments:\n");
172  fi;
173  if verbos > 0 then
174    Print("#I  Total: ", Length(methods)," entries\n");
175  fi;
176  for i in [1..Length(methods)] do
177    m := methods[i];
178    nam:=m.info;
179    val:=m.rank;
180    oper:=m.func;
181    if verbos>1 then
182      Print("#I  Method ",i,": ``",nam,"''");
183      if IsBound(m.location) then
184        Print(" at ", m.location[1], ":", m.location[2]);
185      elif LocationFunc(oper) <> "" then
186        Print(" at ",LocationFunc(oper));
187      fi;
188      Print(", value: ");
189      Print_Value_SFF(val);
190      Print("\n");
191    fi;
192    flag:=true;
193    j:=1;
194    while j<=narg and (flag or verbos>3) do
195      if j=1 and isconstructor then
196	flag2:=IS_SUBSET_FLAGS(m.argFilt[j],flags[j]);
197      else
198	flag2:=IS_SUBSET_FLAGS(flags[j],m.argFilt[j]);
199      fi;
200      flag:=flag and flag2;
201      if flag2=false and verbos>2 then
202	need:=NamesFilter(m.argFilt[j]);
203	if j=1 and isconstructor then
204	  Print("#I   - ",Ordinal(j)," argument must be ",
205		need,"\n");
206	else
207	  has:=NamesFilter(flags[j]);
208	  Print("#I   - ",Ordinal(j)," argument needs ",
209		Filtered(need,i->not i in has),"\n");
210	fi;
211      fi;
212      j:=j+1;
213    od;
214    if flag then
215      if fams=fail or CallFuncList(m.famPred,fams) then
216	if verbos=1 then
217	  Print("#I  Method ",i,": ``",nam,"''");
218          if IsBound(m.location) then
219            Print(" at ", m.location[1], ":", m.location[2]);
220	  elif LocationFunc(oper) <> "" then
221	    Print(" at ",LocationFunc(oper));
222	  fi;
223	  Print(" , value: ");
224	  Print_Value_SFF(val);
225	  Print("\n");
226	fi;
227	if verbos>5 then
228	  Print("#I  Function Body:\n");
229	  Print(oper);
230	fi;
231	if skip=0 then
232	  return oper;
233	else
234	  Add(erg,oper);
235	  skip:=skip-1;
236	  if verbos>0 then
237	    Print("#I  Skipped:\n");
238	  fi;
239        fi;
240      elif verbos>2 then
241        Print("#I   - bad family relations\n");
242      fi;
243    fi;
244  od;
245  if skip<0 then
246    return erg;
247  else
248    return fail;
249  fi;
250end);
251
252BIND_GLOBAL("ApplicableMethod",function(arg)
253local i,l;
254  if Length(arg)<2 or not IsList(arg[2]) or not IsFunction(arg[1]) then
255    Error("usage: ApplicableMethod(<opr>,<arglist>[,<verbosity>[,<nr>]])");
256  fi;
257  l:=ShallowCopy(arg[2]);
258  for i in [1..Length(l)] do
259    if i=1 and IS_CONSTRUCTOR(arg[1]) then
260      l[i]:=l[i];
261    else
262      l[i]:=TypeObj(l[i]);
263  fi;
264  od;
265  arg[2]:=l;
266  return CallFuncList(ApplicableMethodTypes,arg);
267end);
268
269#############################################################################
270##
271#F  ShowImpliedFilters( <filter> )
272##
273##  <#GAPDoc Label="ShowImpliedFilters">
274##  <ManSection>
275##  <Func Name="ShowImpliedFilters" Arg='filter'/>
276##
277##  <Description>
278##  Displays information about the filters that may be
279##  implied by <A>filter</A>. They are given by their names.
280##  <Ref Func="ShowImpliedFilters"/> first displays the names of all filters
281##  that are unconditionally implied by <A>filter</A>. It then displays
282##  implications that require further filters to be present (indicating
283##  by <C>+</C> the required further filters).
284##  <Example><![CDATA[
285##  gap> ShowImpliedFilters(IsNilpotentGroup);
286##  Implies:
287##     IsListOrCollection
288##     IsCollection
289##     IsDuplicateFree
290##     IsExtLElement
291##     CategoryCollections(IsExtLElement)
292##     IsExtRElement
293##     CategoryCollections(IsExtRElement)
294##     CategoryCollections(IsMultiplicativeElement)
295##     CategoryCollections(IsMultiplicativeElementWithOne)
296##     CategoryCollections(IsMultiplicativeElementWithInverse)
297##     IsGeneralizedDomain
298##     IsMagma
299##     IsMagmaWithOne
300##     IsMagmaWithInversesIfNonzero
301##     IsMagmaWithInverses
302##     IsAssociative
303##     HasMultiplicativeNeutralElement
304##     IsGeneratorsOfSemigroup
305##     IsSimpleSemigroup
306##     IsRegularSemigroup
307##     IsInverseSemigroup
308##     IsCompletelyRegularSemigroup
309##     IsGroupAsSemigroup
310##     IsMonoidAsSemigroup
311##     IsOrthodoxSemigroup
312##     IsSupersolvableGroup
313##     IsSolvableGroup
314##     IsNilpotentByFinite
315##
316##
317##  May imply with:
318##  +IsFinitelyGeneratedGroup
319##     IsPolycyclicGroup
320##
321##  ]]></Example>
322##  </Description>
323##  </ManSection>
324##  <#/GAPDoc>
325##
326BIND_GLOBAL("ShowImpliedFilters",function(filter)
327  local flags, implied, f, extra_implications, implication, name, diff_reqs,
328        diff_impls, reduced;
329
330  flags:=FLAGS_FILTER(filter);
331  implied := WITH_IMPS_FLAGS(flags);
332  atomic readonly IMPLICATIONS_SIMPLE do
333    # select all implications which involved <filter> in the requirements
334    f:=Filtered(IMPLICATIONS_SIMPLE, x->IS_SUBSET_FLAGS(x[2],flags));
335    Append(f, Filtered(IMPLICATIONS_COMPOSED, x->IS_SUBSET_FLAGS(x[2],flags)));
336  od; # end atomic
337
338  extra_implications:=[];
339  for implication in f do
340    # the additional requirements
341    diff_reqs:=SUB_FLAGS(implication[2],flags);
342    if SIZE_FLAGS(diff_reqs) = 0 then
343      Assert(0, IS_SUBSET_FLAGS(implied,implication[1]));
344      continue;
345    fi;
346    # the combined implications...
347    diff_impls:=implication[1];
348    # ... minus those implications that already follow from <filter>
349    diff_impls:=SUB_FLAGS(diff_impls,implied);
350    # ... minus those implications that already follow from diff_reqs
351    diff_impls:=SUB_FLAGS(diff_impls,WITH_IMPS_FLAGS(diff_reqs));
352    if SIZE_FLAGS(diff_impls) > 0 then
353      Add(extra_implications, [diff_reqs, diff_impls]);
354    fi;
355  od;
356
357  # remove "obvious" implications
358  if IS_ELEMENTARY_FILTER(filter) then
359    implied := SUB_FLAGS(implied, flags);
360  fi;
361
362  reduced:= function( trues )
363    atomic readonly FILTER_REGION do
364      return Filtered( trues,
365      i -> not ( INFO_FILTERS[i] in FNUM_TPRS
366                 and FLAG1_FILTER( FILTERS[i] ) in trues ) );
367    od;
368  end;
369
370  if SIZE_FLAGS(implied) > 0 then
371    Print("Implies:\n");
372    for name in NamesFilter( reduced( TRUES_FLAGS( implied ) ) ) do
373      Print("   ",name,"\n");
374    od;
375  fi;
376
377  if Length(extra_implications) > 0 then
378    Print("\n\nMay imply with:\n");
379    for implication in extra_implications do
380      for name in NamesFilter( reduced( TRUES_FLAGS( implication[1] ) ) ) do
381        Print("+",name,"\n");
382      od;
383      for name in NamesFilter( reduced( TRUES_FLAGS( implication[2] ) ) ) do
384        Print("   ",name,"\n");
385      od;
386      Print("\n");
387    od;
388  fi;
389end);
390
391
392#############################################################################
393##
394#F  ShowDeclarationsOfOperation( <oper> )
395##
396##  <#GAPDoc Label="ShowDeclarationsOfOperation">
397##  <ManSection>
398##  <Func Name="ShowDeclarationsOfOperation" Arg='oper'/>
399##
400##  <Description>
401##  Displays information about all declarations of the operation <A>oper</A>,
402##  including the location of each declaration and the argument filters.
403##  <Log><![CDATA[
404##  gap> ShowDeclarationsOfOperation(IsFinite);
405##  Available declarations for operation <Property "IsFinite">:
406##    1: GAPROOT/lib/coll.gd:1451 with 1 arguments, and filters [ IsListOrCollection ]
407##    2: GAPROOT/lib/float.gd:212 with 1 arguments, and filters [ IsFloat ]
408##    3: GAPROOT/lib/ctbl.gd:1195 with 1 arguments, and filters [ IsNearlyCharacterTable ]
409##  ]]></Log>
410##  </Description>
411##  </ManSection>
412##  <#/GAPDoc>
413##
414BIND_GLOBAL("ShowDeclarationsOfOperation",function(oper)
415    local locs, reqs, i, r;
416    if not IsOperation(oper) then
417        Error("<oper> must be an operation");
418    fi;
419    Print("Available declarations for operation ", oper, ":\n");
420    locs := GET_DECLARATION_LOCATIONS(oper);
421    if locs = fail then
422        return;
423    fi;
424    reqs := GET_OPER_FLAGS(oper);
425    for i in [1.. Length(locs)] do
426        r := List(reqs[i], r -> JoinStringsWithSeparator(NamesFilter(r), " and \c"));
427        Print(String(i, 3), ": ", locs[i][1], "\c:", locs[i][2], "\c",
428              " with ", Length(reqs[i]), "\c",
429              " arguments, and filters [ ", "\c",
430              JoinStringsWithSeparator(r, ", "),
431              " ]\n"
432              );
433    od;
434end);
435
436
437#############################################################################
438##
439#F  PageSource( func ) . . . . . . . . . . . . . . . show source code in pager
440##
441##  <#GAPDoc Label="PageSource">
442##  <ManSection>
443##  <Func Name="PageSource" Arg='func[, nr]'/>
444##
445##  <Description>
446##  This shows the file containing the source code of the function or method
447##  <A>func</A> in a pager (see <Ref Func="Pager"/>). The display starts at
448##  a line shortly before the code of <A>func</A>.<P/>
449##
450##  For operations <A>func</A> the function shows the source code of the
451##  declaration of <A>func</A>. Operations can have several declarations, use
452##  the optional second argument to specify which one should be shown (in the
453##  order the declarations were read); the default is to show the first.<P/>
454##
455##  For kernel functions the function tries to show the C source code.<P/>
456##
457##  If GAP cannot find a file containing the source code this will be indicated.
458##  <P/>
459##  Usage examples:<Br/>
460##  <C>met := ApplicableMethod(\^, [(1,2),2743527]); PageSource(met);</C><Br/>
461##  <C>PageSource(Combinations);</C><Br/>
462##  <C>PageSource(SORT_LIST); </C><Br/>
463##  <C>PageSource(Size, 2);</C><Br/>
464##  <C>ct := CharacterTable(Group((1,2,3))); </C><Br/>
465##  <C>met := ApplicableMethod(Size,[ct]); PageSource(met); </C>
466##  <P/>
467##  </Description>
468##  </ManSection>
469##  <#/GAPDoc>
470BIND_GLOBAL("PageSource", function ( fun, nr... )
471    local f, n, l, s, ss, locs;
472
473    if Length(nr) > 0 and IsPosInt(nr[1]) then
474      n := nr[1];
475    else
476      n := 1;
477    fi;
478    l := fail;
479    f := FILENAME_FUNC( fun );
480    if IsString(f) and Length(f)>0 and f[1] <> '/' then
481      # first assume it is a local path, otherwise look in GAP roots
482      if not IsReadableFile(f) then
483        if Length(f) > 7 and f{[1..8]} = "GAPROOT/" then
484          f := f{[9..Length(f)]};
485        fi;
486        f := Filename(List(GAPInfo.RootPaths, Directory), f);
487      fi;
488    fi;
489    if f = fail and fun in OPERATIONS then
490      # for operations we show the location(s) of their declaration
491      locs := GET_DECLARATION_LOCATIONS(fun);
492      if n > Length(locs) then
493        Print("Operation ", NameFunction(fun), " has only ",
494              Length(locs), " declarations.\n");
495        return;
496      else
497        if Length(locs) > 1 then
498          Print("Operation ", NameFunction(fun), " has ",
499                Length(locs), " declarations, showing number ", n, ".\n");
500        fi;
501        f := locs[n][1];
502        l := locs[n][2];
503      fi;
504    fi;
505    if f <> fail then
506        if l = fail then
507          l := STARTLINE_FUNC( fun );
508          if l <> fail then
509              l := Maximum(l-5, 1);
510          elif IsKernelFunction(fun) then
511              # page correct C source file and try to find line in C
512              # source starting `Obj Func<fun>`
513              s := String(fun);
514              ss:=SplitString(s,""," <>");
515              s := First(ss, a-> ':' in a);
516              if s <> fail then
517                ss := SplitString(s,":","");
518                l := Concatenation("/Obj *Func", ss[2]);
519              fi;
520          fi;
521        fi;
522    fi;
523    if f = fail or l = fail then
524        if IsKernelFunction(fun) then
525          Print("Cannot locate source of kernel function ",
526                 NameFunction(fun),".\n");
527        else
528          Print( "Source not available.\n" );
529        fi;
530    elif not (IsExistingFile(f) and IsReadableFile(f)) then
531        Print( "Cannot access code from file \"",f,"\".\n");
532    else
533        Print( "Showing source in ", f, " (from line ", l, ")\n" );
534        Pager(rec(lines := StringFile(f), formatted := true, start := l));
535    fi;
536end);
537