Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/GAP/pkg/xgap/lib/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 14.8.2025 mit Größe 104 kB image not shown  

Quellverzeichnis  ilatgrp.gi   Sprache: unbekannt

 
#############################################################################
##
#W  ilatgrp.gi                  XGAP library                  Max Neunhoeffer
##
##
#Y  Copyright 1998,       Max Neunhoeffer,              Aachen,       Germany
##
##  This file contains the implementations for graphs and posets
##

#############################################################################
##
##  Some little gimmicks to fix a bug in gap4b5fix3:
##
##  FIXMEFIXMEFIXMEFIXMEFIXMEFIXMEFIXMEFIXMEFIXMEFIXMEFIXMEFIXMEFIXMEFIXME
##
#############################################################################
InstallTrueMethod(CanComputeSize,IsPermGroup);
InstallTrueMethod(CanComputeSize,CanEasilyComputePcgs);

# prevent an error message with gap4b5:
if not IsBound(EpimorphismPGroup) then
  EpimorphismPGroup := function(arg) 
    Error("not yet implemented in GAP4b5");
  end;
fi;

# prevent an error message when the small groups library is not there:
if not IsBound(HasIdGroup) then
    HasIdGroup := ReturnFalse;
fi;

#############################################################################
##
##  Logging facilities:
##
#############################################################################

GGLLogFile := false;   # not yet logging

BindGlobal( "GGLChooseLog", function(arg)
  local   di,  command;
  if GGLLogFile <> false then
    GGLLogFile := false;
  fi;
  di := Dialog("Filename","Log File?");
  GGLLogFile := Query(di,"xgap.log");
  if GGLLogFile <> false then
    AppendTo(GGLLogFile,"Log of XGAP Session of ");
    command := "date >>";
    Append(command,GGLLogFile);
    Exec(command);
    AppendTo(GGLLogFile,"\n");
  fi;
end);


BindGlobal( "GGLStopLog", function(arg)
  GGLLogFile := false;
end);


#############################################################################
##
##  Representations:  
##
#############################################################################
  
  
#############################################################################
##
#R  IsGraphicSubgroupLattice . . . . . .  repr. for graphic subgroup lattices
##
if not IsBound(IsGraphicSubgroupLattice) then
  DeclareRepresentation( "IsGraphicSubgroupLattice",
    IsComponentObjectRep and IsAttributeStoringRep and IsGraphicSheet and
    IsGraphicSheetRep and IsGraphicGraphRep and IsGraphicPosetRep,
# we inherit those components from the sheet:        
    [ "name", "width", "height", "gapMenu", "callbackName", "callbackFunc",
      "menus", "objects", "free",
# and the following from being a poset:
      "levels",           # list of levels, stores current total ordering
      "levelparams",      # list of level parameters
      "selectedvertices", # list of selected vertices
      "menutypes",        # one entry per menu which contains list of types
      "menuenabled",      # one entry per menu which contains list of flags
      "rightclickfunction",    # the current function which is called when
                               # user clicks right button
      "color",            # some color infos for the case of different models
      "levelboxes",       # little graphic boxes for the user to handle levels
      "showlevels",       # flag, if levelboxes are shown
# now follow our own components:
      "group",            # the group
      "limits",           # a record with some limits, e.g. "conjugates"
      "menuoperations",   # configuration of menu operations
      "infodisplays",     # list of records for info displays, see below
      "largestlabel",     # largest used number for label
      "lastresult",       # list of vertices which are "green"
      "largestinflevel",  # largest used number for infinity-level
      "selector",         # the current text selector or "false"
      "WholeGroupVert",   # Vertex of the whole group
      "TrivialGroupVert"],# Vertex of the trivial subgroup
    IsGraphicSheet );
fi;


#############################################################################
##
##  Configuration section for menu operations and info displays:
##
#############################################################################

#############################################################################
##
##  Some global constants for configuration purposes (see "ilatgrp.gi"):
##
#############################################################################

BindGlobal( "GGLfrom1", 1 );
BindGlobal( "GGLfrom2", 2 );
BindGlobal( "GGLfromSet", 3 );
BindGlobal( "GGLfromAny", 4 );
BindGlobal( "GGLto0", 0 );
BindGlobal( "GGLto1", 1 );
BindGlobal( "GGLtoSet", 2 );
BindGlobal( "GGLwhereUp", 1 );
BindGlobal( "GGLwhereDown", 2 );
BindGlobal( "GGLwhereAny", 0 );
BindGlobal( "GGLwhereBetween", 3 );
BindGlobal( "GGLrelsMax", 1 );
BindGlobal( "GGLrelsTotal", 2 );
BindGlobal( "GGLrelsNo", 0 );
BindGlobal( "GGLrelsDown", 3 );
BindGlobal( "GGLrelsUp", 4 );


#############################################################################
##
#F  GGLClosureGroup( <grp1>, <grp2>, ... ) . . . . . . calculates the Closure
#F  GGLClosureGroup( <grplist> ) . . . . . . . . . . . calculates the Closure
##
##  This function calculates the closure of a number of groups. It uses
##  ClosureGroup inductively. The groups can be specified as multiple 
##  or as one list of groups.
##
BindGlobal( "GGLClosureGroup",
  function(arg)
    local grp,  i;
    if Length(arg) = 1 and IsList(arg[1]) then
      arg := arg[1];
    fi;

    # the number of arguments will always be at least 1!
    grp := arg[1];
    for i in [2..Length(arg)] do
      grp := ClosureGroup(grp,arg[i]);
    od;
    return grp;
  end );
  
  
#############################################################################
##
#F  GGLStringGroup( <G> ) . . . . . . . generates string that describes group  
##
##  This function generates a string that represents a group. It is mainly
##  intended for fp groups and is actually ``stolen'' from some of the 
##  `ViewObj' methods for fp groups. It covers also the case of free groups.
##  Note that the special case of G being a string, which is handled
##  first comes in handy, if functions return a warning instead of a group.
##
BindGlobal( "GGLStringGroup",

function(G)
  local st,stream;

  st := "";
  stream := OutputTextString(st,false);
  PrintTo(stream,G);
  CloseStream(stream);
  return st;
end );

#BindGlobal( "GGLStringGroup", 
#        
#function(G)
#  
#  local st;   # used to build up the string
#  
#  # Is this already a string?
#  if IsString(G) then
#    return G;
#  fi;
#  
#  if IsFreeGroup(G) then 
#    st := "<free group";
#    if IsGroupOfFamily( G )  then
#      if Length( GeneratorsOfGroup( G ) ) > 6  then
#        Append(st," with ");
#        Append(st,String(Length( GeneratorsOfGroup( G ) ) ));
#        Append(st," generators>" );
#      else
#        Append(st," on the generators ");
#        Append(st,String(List(GeneratorsOfGroup( G ),UnderlyingElement)));
#        Append(st,">" );
#      fi;
#    else
#      st := "Group(";
#      if HasGeneratorsOfGroup( G )  then
#        if not IsBound( G!.gensWordLengthSum )  then
#          G!.gensWordLengthSum 
#            := Sum( List( GeneratorsOfGroup( G ), Length ) );
#        fi;
#        if G!.gensWordLengthSum <= 20  then
#          Append(st,String(List(GeneratorsOfGroup( G ),UnderlyingElement)));
#        else
#          Append(st,"<");
#          Append(st,String(Length( GeneratorsOfGroup( G ) )));
#          Append(st," generators>");
#        fi;
#      else
#        Append(st,", no generators known>" );
#      fi;
#      Append(st,")");
#    fi;
#  else  # no free group
#    if IsGroupOfFamily(G) then
#      st := "<fp group";
#      if HasSize(G) then
#        Append(st," of size ");
#        Append(st,String(Size(G)));
#      fi;
#      if Length(GeneratorsOfGroup(G)) > 6 then
#        Append(st," with ");
#        Append(st,String(Length(GeneratorsOfGroup(G))));
#        Append(st," generators>");
#      else
#        Append(st," on the generators ");
#        Append(st,String(List(GeneratorsOfGroup(G),UnderlyingElement)));
#        Append(st,">");
#      fi;
#    else
#      st := "Group(";
#      if HasGeneratorsOfGroup(G) then
#        if not IsBound(G!.gensWordLengthSum) then
#          G!.gensWordLengthSum:=Sum(List(GeneratorsOfGroup(G),
#                                        i->Length(UnderlyingElement(i))));
#        fi;
#        if G!.gensWordLengthSum <= 20 then                                  
#          Append(st,String(List(GeneratorsOfGroup(G),UnderlyingElement)));
#        else
#          Append(st,"<");
#          Append(st,String(Length(GeneratorsOfGroup(G))));
#          Append(st," generators>");
#        fi;
#      else
#        Append(st,"<fp, no generators known>");
#      fi;
#      Append(st,")");
#    fi;
#  fi;   # no free group
#  return st;
#end);

  
#############################################################################
##
#F  GGLStringCosetTable( <G> ). generates string that describes a coset table
##
##  This function generates a string that represents a coset table. If the
##  table is small enough it is converted to a string. Otherwise some info
##  is generated.
##
BindGlobal( "GGLStringCosetTable", 
        
function(CT)
  local st;
  if Length(CT) * Length(CT[1]) < 20 then
    return String(CT);
  else
    st := "<";
    Append(st,String(Length(CT)/2));
    Append(st," generators, ");
    Append(st,String(Length(CT[1])));
    Append(st," cosets>");
    return st;
  fi;
end );


#############################################################################
##
#F  GGLStringAbInvs( <invs> ). generates string that describes ab. invariants
##
##  This function generates a string that describes the abelian invariants.
##
BindGlobal( "GGLStringAbInvs", 
        
function(invs)
  if invs = [] then
    return "perfect";
  else
    return String(invs);
  fi;
end );


#############################################################################
##
#F  GGLStringEpimorphism( <G> )  . generates string describing an epimorphism
##
##  This function generates a string that represents an epimorphism. 
##  It just displays an arrow and the image.
##
BindGlobal( "GGLStringEpimorphism", 
        
function(epi)
  local st;

  st := "<epi ->> ";
  Append(st,GGLStringGroup(Image(epi)));
  Append(st,">");
  return st;
end );


#############################################################################
##
#F  GGLFactorGroup( <G>, <N> ) . . . . . . computes factor group, if possible
##
##  This function checks, if <N> is a normal subgroup in <G>. If not, a
##  warning message is returned as a string. Otherwise, the operation
##  FactorGroup is called and the result is returned.
##
BindGlobal( "GGLFactorGroup", 

function(G,N);
  if IsNormal(G,N) then
    return FactorGroup(G,N);
  else
    return "subgroup is not normal";
  fi;
end );


##
##  The configuration of the menu operations works as follows:
##  Every menu operation gets a record with the following entries, which
##  can take on the values described after the colon respectively:
##
##   name     : a string
##   op       : a GAP-Operation for group(s)
##   sheet    : true, false
##   parent   : true, false
##   from     : GGLfrom1, GGLfrom2, GGLfromSet, GGLfromAny
##   to       : GGLto0, GGLto1, GGLtoSet
##   where    : GGLwhereUp, GGLwhereDown, GGLwhereAny, GGLwhereBetween
##   plural   : true, false
##   rels     : GGLrelsMax, GGLrelsTotal, GGLrelsNo, GGLrelsDown, GGLrelsup
##   retsel   : true, false
##
##  Please use always these names instead of actual values because the values
##  of these variables can be subject to changes, especially because they
##  actually should be integers rather than strings.
##
##  <name> is the name appearing in the menu and info messages.
##  <op> is called to do the real work. The usage of <op> is however configured
##  by the other entries. <from> says, how many groups <op> gets as parameters.
##  It can be one group, exactly two, a list (GGLfromSet) of groups, or
##  a possibly empty list (GGLfromAny).
##  <sheet> says, if the graphic sheet is supplied as first parameter.
##  <parent> says, if the parent group is supplied as first/second parameter of
##  the call of the operation or not.
##  <to> says, how many groups <op> produces, it can be zero, one or a list
##  of groups (GGLtoSet). <where> determines what is known about the relative
##  position of the new groups with respect to the input groups of <op>.
##  GGLwhereUp means, that the new group(s) all contain all groups <op> was
##  called with. GGLwhereDown means, that the new group(s) are all contained
##  in all groups <op> was called with. GGLwhereAny means that nothing is
##  known about the result(s) with respect to this question. GGLwhereBetween
##  applies only for the case <from>=GGLfrom2 and means, that all produced
##  groups are contained in the first group and contain the second group
##  delivered to <op>. That means that in case such an operation exists
##  it will be checked before the call to the operation, which group is
##  contained in the other! It is an error if that is not the case!
##  <plural> is a flag which determines, if more than the
##  appropriate number of vertices can be selected. In this case <op> is called
##  for all subsets of the set of selected subgroups with the right number of
##  groups. This does not happen if <plural> is false. <rels> gives <op> the
##  possibility to return inclusion information about the newly calculated
##  subgroups. If <rels> is GGLrelsMax or GGLrelsTotal then <op> must return
##  a record with components `subgroups' which is a list of subgroups 
##  generated as well as a component `inclusions' which lists all maximality
##  inclusions among these subgroups.
##  A maximality inclusion is given as a list `[<i>,<j>]' indicating that
##  subgroup number <i> is a maximal subgroup of subgroup number <j>, the
##  numbers 0 and 1+length(`subgroups') are used to denote <U> and <G>
##  respectively, this applies to the case <rels>=GGLrelsMax.
##  In the case <rels>=GGLrelsTotal each pair says that the first group is
##  contained in the second. 
##  Again: The complete poset information must be returned!
##  In the case <rels>=GGLrelsNo nothing is known about the relative inclusions
##  of the results. <op> just returns a list of groups. If <rels>=GGLrelsDown
##  then the returned list is a descending chain and if <rels>=GGLrelsUp then
##  the returned list is an ascending chain.
##  If the record component "givesconjugates" is bound to true, then all
##  new vertices are put in the same class as the input vertex, so this
##  only makes sense for <from>=GGLfrom1. It is also only necessary for
##  those group types, where we don't have CanCompareSubgroups.
##  If retsel is bound and set to true, GGLMenuOps will return the groups
##  produced by the operation.


##  we have two cases up to now:
BindGlobal( "GGLMenuOpsForFiniteGroups",
        [ rec( name := "All Subgroups", 
               op := function(G) 
                 local result,cl;
                 result := [];
                 for cl in LatticeSubgroups(G)!.conjugacyClassesSubgroups do
                   Append(result,AsList(cl));
                 od;
                 return result;
               end,
               parent := false, from := GGLfrom1, to := GGLtoSet, 
               where := GGLwhereDown, plural := false, rels := GGLrelsNo ),
          rec( name := "Centralizers", op := Centralizer, 
               parent := true, from := GGLfrom1, to := GGLto1, 
               where := GGLwhereAny, plural := true, rels := GGLrelsNo ),
          rec( name := "Centres", op := Centre, 
               parent := false, from := GGLfrom1, to := GGLto1, 
               where := GGLwhereDown, plural := true, rels := GGLrelsNo ),
          rec( name := "Closure", op := GGLClosureGroup, 
               parent := false, from := GGLfromSet, to := GGLto1, 
               where := GGLwhereUp, plural := false, rels := GGLrelsNo ),
          rec( name := "Closures", op := ClosureGroup, 
               parent := false, from := GGLfrom2, to := GGLto1, 
               where := GGLwhereUp, plural := true, rels := GGLrelsNo ),
          rec( name := "Commutator Subgroups", op := CommutatorSubgroup,
               parent := false, from := GGLfrom2, to := GGLto1, 
               where := GGLwhereAny, plural := true, rels := GGLrelsNo ),
          rec( name := "Conjugate Subgroups", 
               op := function(G,H) 
                       return AsList(ConjugacyClassSubgroups(G,H)); 
                     end,
               parent := true, from := GGLfrom1, to := GGLtoSet, 
               where := GGLwhereAny, plural := true, rels := GGLrelsNo ),
          rec( name := "Cores", op := Core,
               parent := true, from := GGLfrom1, to := GGLto1, 
               where := GGLwhereDown, plural := true, rels := GGLrelsNo ),
          rec( name := "DerivedSeries", op := DerivedSeriesOfGroup,
               parent := false, from := GGLfrom1, to := GGLtoSet, 
               where := GGLwhereDown, plural := true, rels := GGLrelsDown ),
          rec( name := "DerivedSubgroups", op := DerivedSubgroup,
               parent := false, from := GGLfrom1, to := GGLto1, 
               where := GGLwhereDown, plural := true, rels := GGLrelsNo ),
          rec( name := "Fitting Subgroups", op := FittingSubgroup,
               parent := false, from := GGLfrom1, to := GGLto1, 
               where := GGLwhereDown, plural := true, rels := GGLrelsNo ),
          rec( name := "Intermediate Subgroups", op := IntermediateSubgroups,
               parent := false, from := GGLfrom2, to := GGLtoSet, 
               where := GGLwhereBetween, plural := false, rels := GGLrelsMax),
          rec( name := "Intersection", op := Intersection,
               parent := false, from := GGLfromSet, to := GGLto1, 
               where := GGLwhereDown, plural := false, rels := GGLrelsNo ),
          rec( name := "Intersections", op := Intersection,
               parent := false, from := GGLfrom2, to := GGLto1, 
               where := GGLwhereDown, plural := true, rels := GGLrelsNo ),
          rec( name := "Normalizers", op := Normalizer,
               parent := true, from := GGLfrom1, to := GGLto1, 
               where := GGLwhereUp, plural := true, rels := GGLrelsNo ),
          rec( name := "Normal Closures", op := NormalClosure,
               parent := true, from := GGLfrom1, to := GGLto1, 
               where := GGLwhereUp, plural := true, rels := GGLrelsNo ),
          rec( name := "Normal Subgroups", op := NormalSubgroups,
               parent := false, from := GGLfrom1, to := GGLtoSet, 
               where := GGLwhereDown, plural := true, rels := GGLrelsNo ),
          rec( name := "Sylow Subgroups", op := GGLSylowSubgroup,
               parent := false, from := GGLfrom1, to := GGLto1, 
               where := GGLwhereDown, plural := true, rels := GGLrelsNo ),
          rec( name := "SelectedGroups to GAP", 
               op := function(arg) 
                 # We start GAP-Logging if XGAP-Logging is on!
                 if GGLLogFile <> false then
                   LogTo(GGLLogFile);
                 fi;
               end,
               parent := false, sheet := true, retsel := true,
               from := GGLfromSet, to := GGLto0,
               where := GGLwhereAny, plural := false, rels := GGLrelsNo ),
          rec( name := "InsertVertices from GAP",
               op := function(arg) 
                 local v;
                 # We stop the GAP-Logging:
                 if GGLLogFile <> false then
                   LogTo();
                 fi;
                 v := last;
                 if not IsList(v) then
                   if IsGroup(v) then
                     return [v];
                   else
                     return [];
                   fi;
                 else
                   return Filtered(v,x->IsGroup(x));
                 fi;
               end,
               parent := false, sheet := false,
               from := GGLfromAny, to := GGLtoSet,
               where := GGLwhereAny, plural := false, rels := GGLrelsNo ),
          rec( name := "Start Logging", op := GGLChooseLog,
               parent := false, sheet := false, retsel := false,
               from := GGLfromAny, to := GGLto0,
               where := GGLwhereAny, plural := false, rels := GGLrelsNo ),
          rec( name := "Stop Logging", op := GGLStopLog,
               parent := false, sheet := false, retsel := false,
               from := GGLfromAny, to := GGLto0,
               where := GGLwhereAny, plural := false, rels := GGLrelsNo )
] );
                                             
BindGlobal( "GGLMenuOpsForFpGroups",
        [ rec( name := "Abelian Prime Quotient", op := GGLAbelianPQuotient,
               parent := false, from := GGLfrom1, to := GGLto1, 
               where := GGLwhereDown, plural := false, rels := GGLrelsNo,
               sheet := true ),
          rec( name := "All Overgroups", op := IntermediateSubgroups,
               parent := true, from := GGLfrom1, to := GGLtoSet, 
               where := GGLwhereUp, plural := false, rels := GGLrelsMax ),
          rec( name := "Closure", op := GGLClosureGroup, 
               parent := false, from := GGLfromSet, to := GGLto1, 
               where := GGLwhereUp, plural := false, rels := GGLrelsNo ),
          rec( name := "Compare Subgroups", op := GGLCompareSubgroups,
               parent := false, from := GGLfromSet, to := GGLto0,
               where := GGLwhereAny, plural := false, rels := GGLrelsNo,
               sheet := true ),
          rec( name := "Conjugacy Class", 
               op := function(G,H) 
                       local l;
                       l := AsList(ConjugacyClassSubgroups(G,H));
                       return Filtered(l,h->h <> H);
                     end,
               parent := true, from := GGLfrom1, to := GGLtoSet, 
               where := GGLwhereAny, plural := false, rels := GGLrelsNo,
               givesconjugates := true ),
          rec( name := "Cores", op := Core,
               parent := true, from := GGLfrom1, to := GGLto1, 
               where := GGLwhereDown, plural := true, rels := GGLrelsNo ),
          rec( name := "DerivedSubgroups", op := DerivedSubgroup,
               parent := false, from := GGLfrom1, to := GGLto1, 
               where := GGLwhereDown, plural := true, rels := GGLrelsNo ),
          rec( name := "Epimorphisms (GQuotients)", op := GGLEpimorphisms,
               parent := false, from := GGLfrom1, to := GGLtoSet, 
               where := GGLwhereDown, plural := false, rels := GGLrelsNo,
               sheet := true ),
          rec( name := "Intermediate Subgroups", op := IntermediateSubgroups,
               parent := false, from := GGLfrom2, to := GGLtoSet, 
               where := GGLwhereBetween, plural := false, rels := GGLrelsMax ),
          rec( name := "Intersection", op := Intersection,
               parent := false, from := GGLfromSet, to := GGLto1, 
               where := GGLwhereDown, plural := false, rels := GGLrelsNo ),
          rec( name := "Intersections", op := Intersection,
               parent := false, from := GGLfrom2, to := GGLto1, 
               where := GGLwhereDown, plural := true, rels := GGLrelsNo ),
          rec( name := "Low Index Subgroups", op := GGLLowIndexSubgroups,
               parent := false, from := GGLfrom1, to := GGLtoSet,
               where := GGLwhereDown, plural := false, rels := GGLrelsNo,
               sheet := true ),
          rec( name := "Normalizers", op := Normalizer,
               parent := true, from := GGLfrom1, to := GGLto1,
               where := GGLwhereUp, plural := true, rels := GGLrelsNo ),
          rec( name := "Prime Quotient", op := GGLPrimeQuotient,
               parent := false, from := GGLfrom1, to := GGLtoSet, 
               where := GGLwhereDown, plural := false, rels := GGLrelsDown,
               sheet := true ),
          rec( name := "Test Conjugacy", op := GGLTestConjugacy,
               parent := false, from := GGLfromSet, to := GGLto0, 
               where := GGLwhereAny, plural := false, rels := GGLrelsNo,
               sheet := true ),
          rec( name := "SelectedGroups to GAP",
               op := function(arg) 
                 # We start GAP-Logging if XGAP-Logging is on!
                 if GGLLogFile <> false then
                   LogTo(GGLLogFile);
                 fi;
               end,
               parent := false, sheet := true, retsel := true,
               from := GGLfromSet, to := GGLto0,
               where := GGLwhereAny, plural := false, rels := GGLrelsNo ),
          rec( name := "InsertVertices from GAP",
               op := function(arg) 
                 local v;
                 # We stop the GAP-Logging:
                 if GGLLogFile <> false then
                   LogTo();
                 fi;
                 v := last;
                 if not IsList(v) then
                   if IsGroup(v) then
                     return [v];
                   else
                     return [];
                   fi;
                 else
                   return Filtered(v,x->IsGroup(x));
                 fi;
               end,
               parent := false, sheet := false,
               from := GGLfromAny, to := GGLtoSet,
               where := GGLwhereAny, plural := false, rels := GGLrelsNo ),
          rec( name := "Start Logging", op := GGLChooseLog,
               parent := false, sheet := false, retsel := false,
               from := GGLfromAny, to := GGLto0,
               where := GGLwhereAny, plural := false, rels := GGLrelsNo ),
          rec( name := "Stop Logging", op := GGLStopLog,
               parent := false, sheet := false, retsel := false,
               from := GGLfromAny, to := GGLto0,
               where := GGLwhereAny, plural := false, rels := GGLrelsNo )
        ] );


##
##  The configuration of the info displays works as follows:
##  Info displays come in two flavours:
##   (1) info about an attribute
##   (2) info from a function
##  The reason for (2) is that it could be interesting to see "relative"
##  information about a subgroup with respect to the parent group. This
##  cannot be an attribute because it does not belong to the group itself.
##  Every info display gets a record with the following components:
##   name      : a string
##   tostr     : a function (can be "String") which converts the value to 
##               display into a string, if not bound "String" is taken
##  For case (1) we only have one more component:
##   attrib    : an attribute or property (the gap operation)
##  For case (2) we have:
##   func      : a function which returns the value that should be displayed
##   sheet     : true iff first parameter for <func> should be the sheet
##   parent    : true iff first/second parameter should be the parent group
##  if one of the last two is not bound it counts like "false".
##  The information produced by the functions "func" is cached in the record
##  "info" of the "data" part of the vertex under the component "name".
##
BindGlobal( "GGLInfoDisplaysForFiniteGroups",
        [ rec( name := "Size", attrib := Size ),
          rec( name := "Index", func := Index, parent := true ),
          rec( name := "IsAbelian", attrib := IsCommutative ),
          rec( name := "IsCentral", func := IsCentral, parent := true ),
          rec( name := "IsCyclic", attrib := IsCyclic ),
          rec( name := "IsNilpotent", attrib := IsNilpotentGroup ),
          rec( name := "IsNormal", func := IsNormal, parent := true ),
          rec( name := "IsPerfect", attrib := IsPerfectGroup ),
          rec( name := "IsSimple", attrib := IsSimpleGroup ),
          rec( name := "IsSolvable", attrib := IsSolvableGroup ),
        ] );
                 
# Fix the problem with missing small groups library:
if HasIdGroup <> ReturnFalse then
    Add(GGLInfoDisplaysForFiniteGroups,
        rec( name := "Isomorphism", attrib := IdGroup ));
fi; 

    
BindGlobal( "GGLInfoDisplaysForFpGroups",
        [ rec( name := "Index", func := Index, parent := true ),
          rec( name := "IsNormal", func := IsNormal, parent := true ),
          rec( name := "IsFpGroup", func := IsFpGroup, parent := false ),
# FIXME: could that be of any help: (?)
#          rec( name := "IsSubgroupFpGroup", func := IsSubgroupFpGroup, 
#               parent := false ),
          rec( name := "Abelian Invariants", attrib := AbelianInvariants,
               tostr := GGLStringAbInvs ),
          rec( name := "CosetTable", attrib := CosetTableInWholeGroup,
               tostr := GGLStringCosetTable ),
          rec( name := "IsomorphismFpGroup", func := IsomorphismFpGroup,
               parent := false, tostr := GGLStringEpimorphism ),
          rec( name := "FactorGroup", func := GGLFactorGroup, parent := true,
               tostr := GGLStringGroup )
        ] );


#############################################################################
##
##  Global data, menus etc.:  
##
#############################################################################
  

#############################################################################
##
##  Menu entries and Popups:
##
#############################################################################


LastResultOfInfoDisplay := "no info display calculated yet";


############################################################################
##
#M  GGLRightClickPopup . . . . . . . . . . called if user does a right click
##
##  This is called if the user does a right click on a vertex or somewhere
##  else on the sheet. This operation is highly configurable with respect
##  to the Attributes of groups it can display/calculate. See the 
##  configuration section in "ilatgrp.gi" for an explanation.
##
InstallMethod( GGLRightClickPopup,
    "for a graphic subgroup lattice, a vertex or `fail', and two integers",
    true,
    [ IsGraphicSheet and IsGraphicSubgroupLattice, IsObject, IsInt, IsInt ],
    0,

function(sheet,v,x,y)
  local   grp,  textselectfunc,  text,  i,  ii,  str,  funcclose,  funcall,
          maxlengthofname;
  
  # did we get a vertex?
  if v = fail then
    PopupFromMenu(sheet!.menus[3]);
    return;
  fi;
  
    # destroy other text selectors flying around
  if sheet!.selector <> false then
    Close(sheet!.selector);
    sheet!.selector := false;
  fi;
  
  # get the group of <obj>
  grp := v!.data.group;
  
  # how long are the names of the info displays?
  maxlengthofname := Maximum(List(sheet!.infodisplays,i->Length(i.name)));
  
  # text select function
  textselectfunc := function( sel, name )
    local   tid,  current,  text,  str,  value,  parameters,  
            newlevel,  savemaximals,  savemaximalin,  newv,  w;
    
    tid  := sel!.selected;
    current := sheet!.infodisplays[tid];
    text := ShallowCopy(sel!.labels);
    str  := ShallowCopy(String( current.name, -(maxlengthofname+1) ));
    if IsBound(current.attrib) then
      value := current.attrib( grp );
    else
      if not(IsBound(v!.data.info.(current.name))) then
        # we have to calculate:
        parameters := [];
        if IsBound(current.sheet) and current.sheet then 
          Add(parameters,sheet);
        fi;
        if IsBound(current.parent) and current.parent then 
          Add(parameters,sheet!.group);
        fi;
        Add(parameters,grp);
        value := CallFuncList(current.func,parameters);
        v!.data.info.(current.name) := value;
      else
        # we know "by heart"
        value := v!.data.info.(current.name);
      fi;
    fi;
    if IsBound(current.tostr) then
      Append(str,current.tostr(value));
    else
      Append(str,String(value));
    fi;
    text[tid] := str;
    Relabel( sel, text );
    LastResultOfInfoDisplay := value;
    
    # Perhaps the calculation of one attribute triggered the calculation
    # of another one! So we have to look through all infos, if new information
    # is available!
    for i in [1..Length(sheet!.infodisplays)] do
      ii := sheet!.infodisplays[i];
      if IsBound(ii.attrib) and Tester(ii.attrib)(grp) and
         Length(text[i]) >= maxlengthofname+8 and
         text[i]{[maxlengthofname+2..maxlengthofname+8]} = "unknown" then
        # in fact: new information!
        text[i] := text[i]{[1..maxlengthofname+1]};
        if IsBound(ii.tostr) then
          Append(text[i],ii.tostr(ii.attrib(grp)));
        else
          Append(text[i],String(ii.attrib(grp)));
        fi;
        Relabel(sel,text);
      fi;
    od;
    
    # We check, if we have new knowledge about IsNormal:
    if IsBound(current.func) and current.func = IsNormal and
       v!.obj!.shape = VERTEX.rectangle then
      if IsNormal(sheet!.group,v!.data.group) then
        Reshape(v!.obj,VERTEX.diamond);
      else
        Reshape(v!.obj,VERTEX.circle);
      fi;
    fi;
    
    newlevel := false;
    # We check, if we have new knowledge about Index or Size:
    if IsBound(v!.data.info.Index) and v!.data.info.Index <> infinity then
      # if we are not in a finite index level, we take measures:
      if not IsInt(v!.levelparam) or v!.levelparam < 0 then
        newlevel := true;
      fi;
    elif HasSize(v!.data.group) and Size(v!.data.group) <> infinity then
      # if we are not in a finite size level, we take measures:
      if not IsInt(v!.levelparam) then
        newlevel := true;
      fi;
    fi;
    if newlevel then
      # We delete the vertex and reinsert it with all its connections:
      savemaximals := ShallowCopy(v!.maximals);
      savemaximalin := ShallowCopy(v!.maximalin);
      Query(Dialog("OKcancel",
       "Recent results make it necessary to delete vertex and reinsert it!"));
      Delete(sheet,v);
      if IsList(v!.levelparam) then
        DeleteLevel(sheet,v!.levelparam);
      fi;
      
      newv := InsertVertex(sheet,v!.data.group);
      if newv = fail then
        return fail;
      fi;
      # we preserve our knowledge:
      newv[1]!.data.info := v!.data.info;
      v := newv[1];
      for w in savemaximals do
        NewInclusionInfo(sheet,w,v);
      od;
      for w in savemaximalin do
        NewInclusionInfo(sheet,v,w);
      od;
    fi;
      
    return LastResultOfInfoDisplay;
  end;

  # construct the string in the first place:
  text := [];
  for i in sheet!.infodisplays  do
    str := String( i.name, -(maxlengthofname+1) );
    # do we know the value?
    if IsBound(i.attrib) then
      if Tester(i.attrib)(grp) then
        if IsBound(i.tostr) then
          Append(str,i.tostr(i.attrib(grp)));
        else
          Append(str,String(i.attrib(grp)));
        fi;
      else
        Append(str,"unknown");
      fi;
    else   #  its determined by a function and perhaps cached:
      if IsBound(v!.data.info.(i.name)) then
        if IsBound(i.tostr) then
          Append( str, i.tostr(v!.data.info.(i.name)));
        else
          Append( str, String(v!.data.info.(i.name)));
        fi;
      else
        Append( str, "unknown" );
      fi;
    fi;
    Add( text, str );
    Add( text, textselectfunc );
  od;

  # button select functions:
  funcclose := function( sel, bt )
    Close(sel);
    sheet!.selector := false;
    return true;  
  end;
  funcall := function( sel, bt )
    local i;
    for i  in [ 1 .. Length(sel!.labels) ]  do
      sel!.selected := i;
      sel!.textFuncs[i]( sel, sel!.labels[i] );
    od;
    Enable( sel, "all", false );
    return true;  
  end;
  
  # construct text selector
  sheet!.selector := TextSelector(
        Concatenation( " Information about ", v!.label ),
        text,
        [ "all", funcall, "close", funcclose ] );

end);


#############################################################################
##
##  Methods for menu actions:
##
#############################################################################


##
## we need some dialogs:
##
BindGlobal( "GGLPrimeDialog", Dialog( "OKcancel", "Prime" ) );
BindGlobal( "GGLClassDialog", Dialog( "OKcancel", "Class" ) );
BindGlobal( "GGLGoOnDialog", Dialog( "OKcancel", "Go on?" ) );
BindGlobal( "GGLDegreeDialog", Dialog( "OKcancel", "Degree" ) );
BindGlobal( "GGLDimensionDialog", Dialog( "OKcancel", "Dimension" ) );
BindGlobal( "GGLFieldSizeDialog", Dialog( "OKcancel", "Field Size" ) );
BindGlobal( "GGLMaxIndexDialog", Dialog( "OKcancel", "Maximal Index" ) );


#############################################################################
##
#M  GGLMenuOperation . . . . . . . . . . . . . . . .  is called from the menu
##
##  This operation is called for all so called "menu operations" the user
##  wants to perform on lattices. It is highly configurable with respect
##  to the input and output and the GAP-Operation which is actually performed
##  on the selected subgroups. See the configuration section in "ilatgrp.gi"
##  for an explanation.
##
InstallMethod( GGLMenuOperation,
    "for a graphic subgroup lattice, a menu, and a string",
    true,
    [ IsGraphicSheet and IsGraphicSubgroupLattice, IsMenu, IsString ],
    0,

function(sheet, menu, entry)
  local   menuop,  parameters,  selected,  v,  todolist,  i,  j,  
          todo,  currentparameters,  result,  infostr,  vertices,  
          newflag,  len,  hints,  grp,  res,  T,  inc,  T2,  l,  cl;
  
  # first we determine the menu entry which was selected:
  menuop := Position(menu!.entries,entry);
  # fail is not an option here!
  menuop := sheet!.menuoperations[menuop];
  
  # note that we are guaranteed to have enough vertices selected!
  
  # let's prepare the parameters:
  parameters := [];
  if IsBound(menuop.sheet) and menuop.sheet then 
    Add(parameters,sheet); 
  fi;
  if IsBound(menuop.parent) and menuop.parent then 
    Add(parameters,sheet!.group); 
  fi;
  
  # the selected vertices:
  selected := Selected(sheet);
  
  # we clear old "results":
  for v in sheet!.lastresult do
    if IsAlive(v) then
      if PositionSet(selected,v) = fail then
        Recolor(sheet,v,sheet!.color.unselected);
      else
        Recolor(sheet,v,sheet!.color.selected);
      fi;
    fi;
  od;
  sheet!.lastresult := [];
    
  if menuop.from = GGLfrom1 then
    # we do *not* have to look for menuop.plural because if it is false
    # then there can only be one vertex selected!
    todolist := List(selected,v->[v]);
    
  elif menuop.from = GGLfrom2 then
    # we do *not* have to look for menuop.plural because if it is false
    # then there can only be selected exactly two vertices.
    todolist := [];
    for i in [1..Length(selected)-1] do
      for j in [i+1..Length(selected)] do
        Add(todolist,[selected[i],selected[j]]);
      od;
    od;
    
  else   # menuop.from = GGLfromSet or menuop.from = GGLfromAny then
    # we do *not* have to look for menuop.plural because it is forbidden
    # for this case!
    todolist := [selected];
  fi;
  
  for todo in [1..Length(todolist)] do
    currentparameters := ShallowCopy(parameters);
    
    # there is one special case where we have to compare the two groups
    # in question:
    if menuop.from = GGLfrom2 and menuop.where = GGLwhereBetween then
      if not IsSubgroup( todolist[todo][1]!.data.group, 
                         todolist[todo][2]!.data.group ) then
        todolist[todo]{[1,2]} := todolist[todo]{[2,1]};
      fi;
    fi;
    
    if menuop.from = GGLfromSet or menuop.from = GGLfromAny then
      Add(currentparameters,List(todolist[todo],v->v!.data.group));
    else
      Append(currentparameters,List(todolist[todo],v->v!.data.group));
    fi;
    if menuop.to = GGLto0 then
      CallFuncList(menuop.op,currentparameters);
      result := false;
    else
      result := CallFuncList(menuop.op,currentparameters);
    fi;
    
    # we give some information:
    if Length(todolist[todo]) >= 1 then
      infostr := Concatenation(menuop.name," (",todolist[todo][1]!.label);
    else
      infostr := Concatenation(menuop.name," (");
    fi;
    
    for i in [2..Length(todolist[todo])] do
      Append(infostr,",");
      Append(infostr,todolist[todo][i]!.label);
    od;
    Append(infostr,")");
    
    # now we have either nothing or a group or a list of groups or a record 
    # with components "subgroups" and "inclusions".
    if result = fail then
      Append(infostr," --> fail");
      Info(GraphicLattice,1,infostr);
      if GGLLogFile <> false then
        AppendTo(GGLLogFile,infostr,"\n");
      fi;
      infostr := "";
      if Query( GGLGoOnDialog ) = false then
        Info(GraphicLattice,1,"...Aborted.");
        if GGLLogFile <> false then
          AppendTo(GGLLogFile,"...Aborted.\n");
        fi;
        return;
      fi;
    fi;
    if menuop.to = GGLto0 or result = fail then
      if result <> fail then
        Info(GraphicLattice,1,infostr);
        if GGLLogFile <> false then
          AppendTo(GGLLogFile,infostr,"\n");
        fi;
        infostr := "";
      fi;
    else
      
      Append(infostr," --> (");
      
      if menuop.to = GGLto1 then
        result := [result];
      fi;
      
      if IsList(result) then
        result := rec(subgroups := result, inclusions := []);
      fi;
      
      # first we only insert the "new" vertices:
      vertices := [];
      newflag := [];
      len := Length(result.subgroups);
      hints := List(todolist[todo],v->v!.x);
      for grp in [1..len] do
        # we want no lines to vanish:
        if IsBound(menuop.givesconjugates) and
           menuop.givesconjugates then
          res := InsertVertex( sheet, result.subgroups[grp], 
                               todolist[todo][1],hints );
        else
          res := InsertVertex( sheet, result.subgroups[grp], false, hints );
        fi;
        
        if grp <> 1 then
          Append(infostr,",");
        fi;
        if res = fail then
          vertices[grp] := fail;
          newflag[grp] := fail;
          Append(infostr,"fail");
        else
          vertices[grp] := res[1];
          newflag[grp] := res[2];
        
          # we mark the vertex:
          # Select(sheet,res[1],true);
          # as of 1.4.1999 we do no longer select results
          if sheet!.color.result <> false  then
            Recolor( sheet, res[1], sheet!.color.result );
          fi;
          Add( sheet!.lastresult, res[1] );  

          Append(infostr,vertices[grp]!.label);
        fi;
      od;
      Append(infostr,")");
      Info(GraphicLattice,1,infostr);
      if GGLLogFile <> false then
        AppendTo(GGLLogFile,infostr,"\n");
      fi;
      infostr := "";
      
      # if the sheet has the HasseProperty, we are done, because the 
      # connections are calculated. Otherwise we have to see what we can do.
      if not HasseProperty(sheet) then
        # do we have additional information?
        if menuop.rels = GGLrelsTotal then
          # we calculate the info which vertex is maximal in which:
          T := List([1..len],x->List([1..len],y->0));
          for inc in result.inclusions do
            T[inc[1]][inc[2]] := 1;
          od;
          T2 := T * T;
          # if there is a value <> 0 at the position (i,j) then there is a
          # possibility to walk in two steps from vertex i to vertex j
          for i in [1..len] do
            for j in [1..len] do
              if T[i][j] <> 0 and T2[i][j] = 0 then
                if vertices[i] <> fail and vertices[j] <> fail then
                  NewInclusionInfo( sheet, vertices[i], vertices[j] );
                  if not(IsAlive(vertices[i]!.obj)) then
                    vertices[i] := fail;
                  fi;
                  if not(IsAlive(vertices[j]!.obj)) then
                    vertices[j] := fail;
                  fi;
                fi;
              fi;
            od;
          od;
        elif menuop.rels = GGLrelsMax then
          for inc in result.inclusions do
            if inc[1] >= 1 and inc[1] <= len and 
               inc[2] >= 1 and inc[2] <= len then
              # this is no inclusion with lower or higher groups!
              if vertices[inc[1]] <> fail and vertices[inc[2]] <> fail then
                NewInclusionInfo( sheet, vertices[inc[1]], vertices[inc[2]] );
                if not(IsAlive(vertices[inc[1]]!.obj)) then
                  vertices[inc[1]] := fail;
                fi;
                if not(IsAlive(vertices[inc[2]]!.obj)) then
                  vertices[inc[2]] := fail;
                fi;
              fi;
            fi;
          od;
        elif menuop.rels = GGLrelsDown then
          for i in [1..len-1] do
            if vertices[i+1] <> fail and vertices[i] <> fail then
              NewInclusionInfo( sheet, vertices[i+1], vertices[i] );
              if not(IsAlive(vertices[i]!.obj)) then
                vertices[i] := fail;
              fi;
              if not(IsAlive(vertices[i+1]!.obj)) then
                vertices[i+1] := fail;
              fi;
            fi;
          od;
        elif menuop.rels = GGLrelsUp then
          for i in [1..len-1] do
            if vertices[i] <> fail and vertices[i+1] <> fail then
              NewInclusionInfo( sheet, vertices[i], vertices[i+1] );
              if not(IsAlive(vertices[i]!.obj)) then
                vertices[i] := fail;
              fi;
              if not(IsAlive(vertices[i+1]!.obj)) then
                vertices[i+1] := fail;
              fi;
            fi;
          od;
        fi;
        # we cannot say anything if menuop.rels = GGLrelsNo
        
        # perhaps we have information about the selected groups:
        if menuop.where = GGLwhereUp then
          for i in [1..len] do
            for j in [1..Length(todolist[todo])] do
              if vertices[i] <> fail then
                NewInclusionInfo( sheet, todolist[todo][j], vertices[i] );
                if not(IsAlive(vertices[i]!.obj)) then
                  vertices[i] := fail;
                fi;
              fi;
            od;
          od;
        elif menuop.where = GGLwhereDown then
          for i in [1..len] do
            for j in [1..Length(todolist[todo])] do
              if vertices[i] <> fail then
                NewInclusionInfo( sheet, vertices[i], todolist[todo][j] );
                if not(IsAlive(vertices[i]!.obj)) then
                  vertices[i] := fail;
                fi;
              fi;
            od;
          od;
        elif menuop.where = GGLwhereBetween then
          for i in [1..len] do
            if vertices[i] <> fail then
              NewInclusionInfo( sheet, vertices[i], todolist[todo][1] );
              NewInclusionInfo( sheet, todolist[todo][2], vertices[i] );
              if not(IsAlive(vertices[i]!.obj)) then
                vertices[i] := fail;
              fi;
            fi;
          od;
        fi;
        
        # we cannot say anything if menuop.where = GGLwhereAny
        # except: all subgroups are in the whole group and
        #         all subgroups contain the trivial subgroup
        # first we catch the case that one of the new vertices is the
        # trivial subgroup, it always knows its Size 1!
        for i in [1..len] do
          if vertices[i] <> fail and
             HasSize(vertices[i]!.data.group) and
             Size(vertices[i]!.data.group) = 1 then
            sheet!.TrivialGroupVert := vertices[i];
            # We have the trivial subgroup, it is contained in all other
            # subgroups:
            for l in sheet!.levels do
              for cl in l!.classes do
                for v in cl do
                  NewInclusionInfo(sheet, sheet!.TrivialGroupVert, v);
                od;
              od;
            od;
          fi;
        od;
        for i in [1..len] do
          if vertices[i] <> fail then
            if not(IsAlive(vertices[i]!.obj)) then
              vertices[i] := fail;
            else
              if IsAlive(sheet!.WholeGroupVert!.obj) then
                NewInclusionInfo( sheet, vertices[i], sheet!.WholeGroupVert );
              fi;
              if sheet!.TrivialGroupVert <> false and
                 IsAlive(sheet!.TrivialGroupVert!.obj) then
                NewInclusionInfo( sheet, sheet!.TrivialGroupVert, vertices[i]);
              fi;
            fi;
          fi;
        od;
        
      fi;     # not HasseProperty
    fi;  # operation produced something
  od;  # all done
  if IsBound(menuop.retsel) and menuop.retsel = true then
    last := SelectedGroups(sheet);
  fi;
end);


# We remember the last prime the user wanted:
GGLSylowLastPrime := fail;


#############################################################################
##
#M  GGLSylowSubgroup(<grp>)  . . . . . .  asks for prime, calls SylowSubgroup
##
##  This operation just asks for a prime by a little dialog and calls then
##  SylowSubgroup. Returns its result.
##
InstallMethod( GGLSylowSubgroup,
    "for a group",
    true,
    [ IsGroup ],
    0,

function(grp)
  local   res,  p, st;
  
  repeat
    st := "Prime ?";
    if IsInt(GGLSylowLastPrime) then
      st := Concatenation(st," (Default: ",String(GGLSylowLastPrime));
      st := Concatenation(st,")");
    fi;
    
    res := Query( Dialog("OKcancel",st) );
    if res = false then
      return fail;
    fi;
    if IsInt(GGLSylowLastPrime) and res = "" then
      p := GGLSylowLastPrime;
    else
      p := Int(res);
    fi;
    if not IsInt(p) or not IsPrime(p) then
      res := Query(Dialog("OKcancel","You must enter a prime!"));
      if res = false then
        return fail;
      fi;
      res := false;
    else
      res := true;
    fi;
  until res;
  if p <> GGLSylowLastPrime then
    Info(GraphicLattice,1,"Sylow prime: ",p);
    if GGLLogFile <> false then
      AppendTo(GGLLogFile,"Sylow prime: ",p,"\n");
    fi;
  fi;
  GGLSylowLastPrime := p;
  return SylowSubgroup( grp, p );
end);


#############################################################################
##
#M  GGLAbelianPQuotient(<sheet>,<grp>) . . . . . asks for p and calls library
##
##  This operation asks for a prime p and runs then the library operations
##  to calculate abelian prime quotients.
##
InstallMethod( GGLAbelianPQuotient,
    "for a graphic subgroup lattice sheet, and an fp group",
    true,
    [ IsGraphicSubgroupLattice, IsGroup ],
    0,

function(sheet,grp)
  local res, p, epi;
  res := Query( GGLPrimeDialog );
  if res = false then
    return fail;
  fi;
  p := Int(res);
  if not IsInt(p) or not IsPrime(p) then
    Query(Dialog("OKcancel","You must enter a prime!"));
    return fail;
  fi;
  Info(GraphicLattice,1,"AbelianPQuotient prime: ",p);
  if GGLLogFile <> false then
    AppendTo(GGLLogFile,"AbelianPQuotient prime: ",p,"\n");
  fi;
  epi := EpimorphismPGroup( grp, p, 1 );
  # this should be cheap and store the Size in the Image
  # therefore the following Kernel will know its Index in the whole group!
  Size(Image(epi));
  return Kernel(epi);
end);


#############################################################################
##
#M  GGLPrimeQuotient(<sheet>,<grp>) .  asks for p and class and calls library
##
##  This operation asks for a prime p and a class cl and runs then the
##  library operations to calculate prime quotients up to class cl.
##
InstallMethod( GGLPrimeQuotient,
    "for a graphic subgroup lattice sheet, and an fp group",
    true,
    [ IsGraphicSubgroupLattice, IsGroup ],
    0,

function(sheet,grp)
  local res, p, cl, i, l, epi;
  res := Query( GGLPrimeDialog );
  if res = false then
    return fail;
  fi;
  p := Int(res);
  if not IsInt(p) or not IsPrime(p) then
    Query(Dialog("OKcancel","You must enter a prime!"));
    return fail;
  fi;
  Info(GraphicLattice,1,"PQuotient prime: ",p);
  if GGLLogFile <> false then
    AppendTo(GGLLogFile,"PQuotient prime: ",p,"\n");
  fi;
  res := Query( GGLClassDialog );
  if res = false then
    return fail;
  fi;
  cl := Int(res);
  if not IsInt(cl) or not cl >= 1 then
    Query(Dialog("OKcancel","You must enter an integer >= 1!"));
    return fail;
  fi;
  Info(GraphicLattice,1,"PQuotient class: ",cl);
  if GGLLogFile <> false then
    AppendTo(GGLLogFile,"PQuotient class: ",cl,"\n");
  fi;
  l := [];
  for i in [1..cl] do
    epi := EpimorphismPGroup( grp, p, i );
    # this should be cheap and store the Size in the Image
    # therefore the following Kernel will know its Index in the whole group!
    Size(Image(epi));
    Add(l, Kernel(epi) );
  od;
  return l;
end);


#############################################################################
##
#M  GGLKernelQuotientSystem  . . . . . . . calculates the kernel of epi to qs
##
##  obsolete!?!
#FIXME:
##
#InstallMethod( GGLKernelQuotientSystem,
#    "for a quotient system",
#    true,
#    [ IsQuotientSystem ],
#    -5,
#
#function(qs)
#  return Kernel( GGLEpiQuotientSystem(qs) );
#end );


# We store the text selector in this variable to destroy it, if the next one
# pops up.
GGLEpiTextsel := false;

# the vertex we currently work on:
GGLEpiVertex := false;

# The user can supply groups for epimorghisms in the following variable:
IMAGE_GROUP := 0;


#############################################################################
##
#M  GGLEpimorphisms(<sheet>,<grp>) . . . pops up box to choose on which group
##
##  This operations brings up a text selector where one can choose several
##  types of groups to calculate epimorphisms onto.
##
InstallMethod( GGLEpimorphisms,
    "for a graphic subgroup lattice sheet, and (fp) group",
    true,
    [ IsGraphicSubgroupLattice, IsGroup ],
    0,

function(sheet,grp)
  local   GGLEpiResults,  GGLEpi,  GGLEpiShowResult,  GGLEpiShowStab,  
          info,  width,  text,  i,  closefunc,  name, kerneldone, stabdone;
  
  GGLEpiResults := [];   # no results yet
  kerneldone := false;   # we did not yet include the kernels
  stabdone := false;     # we did not yet include point stabilizers
  
  # Here comes the function that allows the user to search for epimorphisms:

  GGLEpi := function(sel,st)
    local   txt,  tid,  len,  res,  deg,  epigrp,  dim,  fis,  vec,  i,  
            path,  str;
    txt := sel!.labels;
    tid := sel!.selected;
    len := Length(txt[tid]);    # we want to preserve this!
    if st{[1..3]} = "Sym" then   # Epis onto a symmetric group:
      res := Query(GGLDegreeDialog); if res = false then return fail; fi;
      deg := Int(res); if not IsInt(deg) or deg < 2 then return fail; fi;
      epigrp := SymmetricGroup(deg);
      txt[tid] := String(Concatenation("Sym(",String(deg),")"),-len);
    elif st{[1..3]} = "Alt" then # Epis onto an alternating group:
      res := Query(GGLDegreeDialog); if res = false then return fail; fi;
      deg := Int(res); if not IsInt(deg) or deg < 2 then return fail; fi;
      epigrp := AlternatingGroup(deg);
      txt[tid] := String(Concatenation("Alt(",String(deg),")"),-len);
    elif st{[1..3]} = "PSL" then
      # Epis onto a PSL:
      res := Query(GGLDimensionDialog); if res = false then return fail; fi;
      dim := Int(res); if not IsInt(dim) or dim < 2 then return fail; fi;
      res := Query(GGLFieldSizeDialog); if res = false then return fail; fi;
      fis := Int(res); if not IsInt(fis) or not IsPrimePowerInt(fis) then 
        return fail; 
      fi;
      epigrp := SL(dim,fis);
      # FIXME: Do we have to go through this???
      vec := [1]; for i in [2..dim] do Add(vec,0); od;
      vec := vec * Z(fis)^0;
      epigrp := Action(epigrp,Orbit(epigrp,vec,OnLines),OnLines);
      txt[tid] := String(Concatenation("PSL(",String(dim),",",
                                       String(fis),")"),-len);
    elif st{[1..3]} = "Lib" then
      # Epis onto a group of our library:
      path := ShallowCopy(Filename(DirectoriesPackageLibrary("xgap", ""), ""));
      Append(path,"pmg/"); # pkg/xgap/pmg/
      res := Query(Dialog("Filename","Which group?"),path);
      if res = false then
        return fail;
      fi;
      IMAGE_GROUP := 0;
      if not READ(res) then
        Info(GraphicLattice,1,Concatenation( "cannot read file ", res ));
        return fail;
      elif IsInt(IMAGE_GROUP) then
        Info(GraphicLattice,1,Concatenation( res, 
                " does not define IMAGE_GROUP" ));
        return fail;
      fi;
      epigrp := IMAGE_GROUP;
      if HasName(epigrp) then
        txt[tid] := String(Concatenation("Library: ",Name(epigrp),")"),-len);
      else
        txt[tid] := String("Library: Group with no name",-len);
      fi;
    elif st{[1..3]} = "Use" or st{[1..3]} = "Def" then
      if not IsBound(IMAGE_GROUP) or IsInt(IMAGE_GROUP)  then
        txt[tid] := "Define IMAGE_GROUP & click here! ";
        Relabel( sel, txt );
        return fail;
      fi;
      epigrp := IMAGE_GROUP;
      if HasName(epigrp) then
        txt[tid] := String(Concatenation("User Defined: ",Name(epigrp),")"),
                           -len);
      else
        txt[tid] := String("User Defined: Group with no name",-len);
      fi;
      IMAGE_GROUP := 0;
    fi;
    
    # now the function has either returned (with "fail" as return value) or
    # epigrp is correctly initialized with a group
    txt[tid]{[len-14..len]} := " computing ... ";
    Relabel(sel,txt);
    GGLEpiResults := GQuotients(GGLEpiVertex!.data.group,epigrp);
    str := Concatenation("       ",String(Length(GGLEpiResults))," found");
    txt[tid]{[len-Length(str)+1..len]} := str;
    Relabel(sel,txt);
    Enable(sel,"display",true);
    if IsPermGroup(epigrp) then
      Enable(sel,"display point stabilizers",true);
    fi;
    return true;
  end;
  
  # The following function is called when the user selects "display". The
  # calculated results are put into the lattice.
  
  GGLEpiShowResult := function(sel,entry)
    local   groups,  g,  v,  txt,  tid,  len,  e;
    for e in GGLEpiResults do
      # this should be cheap and store the Size in the Image
      # therefore the following Kernel will know its Index in the whole group!
      Size(Image(e));
    od;
    groups := List(GGLEpiResults,Kernel);
    kerneldone := [];
    for g in [1..Length(groups)] do
      v := InsertVertex(sheet,groups[g],false,[GGLEpiVertex!.x]);
      if v <> fail then
        # as of 1.4.1999 we do no longer select results:
        # Select(sheet,v[1]);
        kerneldone[g] := v[1];
        if sheet!.color.result <> false  then
          Recolor( sheet, v[1], sheet!.color.result );
        fi;
        Add( sheet!.lastresult, v[1] );
        if v[2] then
          NewInclusionInfo(sheet,v[1],GGLEpiVertex);
          if stabdone <> false and IsBound(stabdone[g]) and
             IsAlive(stabdone[g]!.obj) then
            NewInclusionInfo(sheet,v[1],stabdone[g]);
          fi;
        fi;
      fi;
    od;
    Enable(sel,"display",false);
    txt := sel!.labels;
    tid := sel!.selected;
    len := Length(txt[tid]);
    txt[tid]{[len-13..len]} := "              ";
    Relabel(sel,txt);
    return true;
  end;
  
  GGLEpiShowStab := function(sel,entry)
    local   groups,  g,  v,  txt,  tid,  len;
    groups := List(GGLEpiResults,e -> PreImage(e,Stabilizer(Image(e),1)));
    stabdone := [];
    for g in [1..Length(groups)] do
      v := InsertVertex(sheet,groups[g],false,[GGLEpiVertex!.x]);
      if v <> fail then
        # as of 1.4.1999 we do no longer select results:
        # Select(sheet,v[1]);
        stabdone[g] := v[1];
        if sheet!.color.result <> false  then
          Recolor( sheet, v[1], sheet!.color.result );
        fi;
        Add( sheet!.lastresult, v[1] );
        if v[2] then
          NewInclusionInfo(sheet,v[1],GGLEpiVertex);
          if kerneldone <> false and IsBound(kerneldone[g]) and
             IsAlive(kerneldone[g]!.obj) then
            NewInclusionInfo(sheet,kerneldone[g],v[1]);
          fi;
        fi;
      fi;
    od;
    Enable(sel,"display point stabilizers",false);
    txt := sel!.labels;
    tid := sel!.selected;
    len := Length(txt[tid]);
    txt[tid]{[len-13..len]} := "              ";
    Relabel(sel,txt);
    return true;
  end;
  
  # here starts the code of the operation GGLEpimorphisms:
  
  # get rid of old text selectors:
  if GGLEpiTextsel <> false then
    Close(GGLEpiTextsel);
    GGLEpiTextsel := false;
  fi;
  
  # Store the vertex in question:
  GGLEpiVertex := Selected(sheet)[1];
  
  # construct text describing the groups
  info := [
           "Sym(n)",            GGLEpi,
           "Alt(n)",               GGLEpi,
           "PSL(d,q)",             GGLEpi,
           "Library",              GGLEpi,
           "User Defined",         GGLEpi,
           ];
  width := Maximum(List(info{[1,3..Length(info)-1]},Length))+20;
  text  := [];
  for i  in [ 1, 3 .. Length(info)-1 ]  do
    info[i] := String( info[i], -width );
    Add( text, info[i] );
  od;
  
  # close function
  closefunc := function( sel, bt )
    Close(sel);
    GGLEpiTextsel := false;
    return false;
  end;
  
  # construct a text selector
  if HasName(sheet!.group) then
    name := Name(sheet!.group);
  else
    name := "Group";
  fi;
  
  GGLEpiTextsel :=
    TextSelector(Concatenation(
            "          Epimorphisms from ", name, "           " ),
            info, [ "display", GGLEpiShowResult, "close", closefunc,
                    "display point stabilizers", GGLEpiShowStab ]
            );
  Enable( GGLEpiTextsel, "display", false );
  Enable( GGLEpiTextsel, "display point stabilizers", false );
  return [];
end );


#############################################################################
##
#M  GGLLowIndexSubgroups(<sheet>,<grp>) . .  pops up box to choose max. index
##
##  This operations brings up a dialog, in which one can choose the maximal
##  index for the subgroups that are searched.
##
InstallMethod( GGLLowIndexSubgroups,
    "for a graphic subgroup lattice sheet, and a (fp) group",
    true,
    [ IsGraphicSubgroupLattice, IsGroup ],
    0,

function(sheet,grp)
  local   res,  p;
  res := Query( GGLMaxIndexDialog );
  if res = false then
    return fail;
  fi;
  p := Int(res);
  if not IsInt(p) or p <= 0 then
    return fail;
  fi;
  Info(GraphicLattice,1,"Limit for LowIndex: ",p);
  if GGLLogFile <> false then
    AppendTo(GGLLogFile,"Limit for LowIndex: ",p,"\n");
  fi;
  
  return LowIndexSubgroupsFpGroup(grp,TrivialSubgroup(grp),p);
end);


#############################################################################
##
#M  GGLCompareSubgroups(<sheet>,<grplist>) . . . . .  compares some subgroups
##
##  This operation lets the GAP library compare the selected subgroups.
##  The new information about equality or inclusion of one in the other resp.
##  is included into the graphic lattice. This can lead to the merging of
##  vertices. No new vertices are included into the lattice.
##
InstallMethod( GGLCompareSubgroups,
    "for a graphic subgroup lattice sheet, and a list of fp groups",
    true,
    [ IsGraphicSubgroupLattice, IsList ],
    0,
        
function( sheet, grplist )
  local   vertlist,  poslist,  i,  j,  vert,  pos;
  # we ignore grp1 and grp2 and look at the selected vertices:
  vertlist := Selected(sheet);  # at least one vertex is selected!
  poslist := List(vertlist,v->Position(sheet!.levelparams,v!.levelparam));
  for i in [1..Length(vertlist)] do
    for j in [i+1..Length(vertlist)] do
      if IsAlive(vertlist[i]) and IsAlive(vertlist[j]) then
        # Now the routine for two vertices:
        if poslist[i] < poslist[j] then
          vert := vertlist{[j,i]};
          pos := poslist{[j,i]};
        else
          vert := vertlist{[i,j]};
          pos := poslist{[i,j]};
        fi;

        # first make sure that there is no "way" from v[2] down to v[1] on the 
        # connections which are already in the poset. We use the function
        # GPSearchWay in poset.gi. Documentation there says:
        #   The following function is only internal:
        #   Use it on your own risk and only if you know what you are doing!
        # So I (Max) say:
        #  *I know what I am doing!*
        if not GPSearchWay(sheet, vert[2], vert[1], pos[1]) then 
        # note the order of the vertices in the calling convention!
        # see: I really know what I am doing!
          if IsSubgroup(vert[2]!.data.group,vert[1]!.data.group) then
            Info(GraphicLattice,1,vert[2]!.label," contains ",vert[1]!.label);
            if GGLLogFile <> false then
              AppendTo(GGLLogFile,vert[2]!.label," contains ",
                      vert[1]!.label,"\n");
            fi;
            NewInclusionInfo(sheet,vert[1],vert[2]);
          elif IsSubgroup(vert[1]!.data.group,vert[2]!.data.group) then
            Info(GraphicLattice,1,vert[1]!.label," contains ",vert[2]!.label);
            if GGLLogFile <> false then
              AppendTo(GGLLogFile,vert[1]!.label," contains ",
                       vert[2]!.label,"\n");
            fi;
            NewInclusionInfo(sheet,vert[2],vert[1]);
          fi;
        else
          if vert[1]!.data.group = vert[2]!.data.group then
            # groups are the same!
            MergeVertices(sheet,vert[1],vert[2]);
          fi;
        fi;
      fi;
    od;
  od;
  return;
end );

      
#############################################################################
##
#F  GGLTestConjugacy(<sheet>,<grplist>) . . . . . test conjugacy of subgroups
##
##  This operation lets the GAP library test the selected conjugacy classes
##  of subgroups. If new information about conjugacy is found, classes are
##  merged.
##
InstallMethod( GGLTestConjugacy,
    "for a graphic subgroup lattice sheet, and a list of fp groups",
--> --------------------

--> maximum size reached

--> --------------------

[ Verzeichnis aufwärts0.63unsichere Verbindung  Übersetzung europäischer Sprachen durch Browser  ]