Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  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

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

[ Konzepte0.55Was zu einem Entwurf gehört  Wie die Entwicklung von Software durchgeführt wird  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge