home *** CD-ROM | disk | FTP | other *** search
/ Math Solutions 1995 October / Math_Solutions_CD-ROM_Walnut_Creek_October_1995.iso / pc / mac / discrete / lib / permcser.g < prev    next >
Encoding:
Text File  |  1993-05-05  |  79.2 KB  |  2,144 lines

  1. #############################################################################
  2. ##
  3. #A  permcser.g                  GAP library                       Akos Seress
  4. ##
  5. #H  @(#)$Id: permcser.g,v 3.2 1993/02/10 13:21:58 martin Rel $
  6. ##
  7. #Y  Copyright (c)  1993,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
  8. ##
  9. ##  This file contains  the  functions  to  compute  composition  series  for
  10. ##  permutation groups and related stuff.
  11. ##
  12. #H  $Log: permcser.g,v $
  13. #H  Revision 3.2  1993/02/10  13:21:58  martin
  14. #H  fixed 'PCore' and 'Radical' to accept new output of 'CompositionSeries'
  15. #H
  16. #H  Revision 3.1  1993/02/10  10:14:55  martin
  17. #H  initial revision under RCS
  18. #H
  19. ##
  20.  
  21.  
  22. #############################################################################
  23. ##
  24. #F  PermGroupOps.CompositionSeries( <G> ) . . . . . . . .  composition series
  25. ##
  26. PermGroupOps.CompositionSeries := function( G )
  27.     local   L;
  28.  
  29.     # if <G> is solvable use 'CompositionSeriesSolvablePermGroup'
  30.     if IsSolvable(G)  then
  31.         L := CompositionSeriesSolvablePermGroup(G);
  32.  
  33.     # otherwise call our super function (this will signal an error)
  34.     else
  35.         L := CompositionSeriesPermGroup(G);
  36.     fi;
  37.  
  38.     # return the series <L>
  39.     return L;
  40. end;
  41.  
  42.  
  43. #############################################################################
  44. ##
  45. #F  CompositionSeriesPermGroup(<G>) . composition series of permutation group
  46. ##
  47. ##  'CompositionSeriesPermGroup' returns the composition series of <G>  as  a
  48. ##  list.
  49. ##
  50. ##  The subgroups in this list have a slightly modified 'FactorGroup' method,
  51. ##  which notices if you compute the factor group of one subgroup by the next
  52. ##  and return the factor group as a  primitive  permutation  group  in  this
  53. ##  case (which is also computed by the function below).  The  factor  groups
  54. ##  remember the natural homomorphism since the images of the  generators  of
  55. ##  the subgroup are known and the natural  homomorphism can thus be  written
  56. ##  as 'GroupHomomorphismByImages'.
  57. ##
  58. ##  The program works for  permutation  groups  of  degree  < 2^20 = 1048576.
  59. ##  For higher degrees  'IsSimple'  and  'CasesCSPG'  must  be  extended with
  60. ##  longer lists of primitive  groups  from  extensions  in  Kantor's  tables
  61. ##  (see JSC. 12(1991), pp. 517-526).  It may also be  neccessary  to  modify
  62. ##  'FindNormalCSPG'.
  63. ##
  64. ##  A general reference for the algorithm is:
  65. ##  Beals-Seress, 24th Symp. on Theory of Computing 1992.
  66. ##
  67. CompositionSeriesPermGroup := function(Gr)
  68.     local   normals,    # first component of output; normals[i] contains
  69.                         # generators for i^th subgroup in comp. series
  70.             factors,    # second component of output; factors[i] contains
  71.                         # the action of generators in normals[i]
  72.             factorsize, # third component of output; factorsize[i] is the
  73.                         # size of the i^th factor group
  74.             homlist,    # list of homomorphisms applied to input group
  75.             auxiliary,  # if auxiliary[j] is bounded, it contains
  76.                         # a subg. which must be added to kernel of homlist[j]
  77.             index,      # variable recording how many elements of normals are
  78.                         # computed
  79.             workgroup,  # the subnormal factor group we currently work with
  80.             lastpt,     # degree of workgroup
  81.             tchom,      # transitive constituent homomorphism applied to
  82.                         # intransitive workgroup
  83.             bhom,       # block homomorphism applied to imprimitive workgroup
  84.             bl,         # block system in workgroup
  85.             D,          # derived subgroup of workgroup
  86.             top,        # index of D in workgroup
  87.             g,          # element in a generator list
  88.             lenhomlist, # length of homlist
  89.             i, s,  t,   #
  90.             ops,        # modified operations record
  91.             list;       # output of CompositionSeries
  92.  
  93.     # initialize output and work arrays
  94.     normals := [];
  95.     factors := [];
  96.     factorsize := [];
  97.     auxiliary := [];
  98.     homlist := [];
  99.     index := 1;
  100.     MakeStabChain(Gr);
  101.     workgroup := Gr;
  102.  
  103.     # workgroup is always a factor group of the input Gr such that a
  104.     # composition series for Gr/workgroup is already computed.
  105.     # Try to get a factor group of workgroup
  106.     while (Size(workgroup) > 1) or (Length(homlist) > 0) do
  107.         if Size(workgroup) > 1  then
  108.             lastpt := PermGroupOps.LargestMovedPoint(workgroup);
  109.  
  110.             # if workgroup is not transitive
  111.             if Length(workgroup.orbit) < lastpt   then
  112.                 tchom := OperationHomomorphism(workgroup,
  113.                                      Operation(workgroup,workgroup.orbit));
  114.                 Add(homlist,tchom);
  115.                 workgroup := Image(tchom,workgroup);
  116.             else
  117.                 bl := MaximalBlocks(workgroup,[1..lastpt]);
  118.  
  119.                 # if workgroupo is not primitive
  120.                 if Length(bl) > 1  then
  121.                     bhom := OperationHomomorphism(workgroup,
  122.                                         Operation(workgroup,bl,OnSets));
  123.                     workgroup := Image(bhom,workgroup);
  124.                     Add(homlist,bhom);
  125.                 else
  126.                     D := DerivedSubgroup(workgroup);
  127.                     top := Size(workgroup)/Size(D);
  128.  
  129.                     # if workgroup is not perfect
  130.                     if top > 1  then
  131.  
  132.                         # fill up workgroup/D by cyclic factors
  133.                         index := NonPerfectCSPG(homlist,normals,factors,
  134.                                  auxiliary,factorsize,top,index,D,workgroup);
  135.                         workgroup := D;
  136.  
  137.                     # otherwise chop off simple factor group from top of
  138.                     # workgroup
  139.                     else
  140.                         workgroup := PerfectCSPG(homlist,normals,factors,
  141.                                        auxiliary,factorsize,index,workgroup);
  142.                         index := index+1;
  143.                     fi;  # nonperfect-perfect
  144.  
  145.                 fi;  # primitive-imprimitive
  146.  
  147.             fi;  # transitive-intransitive
  148.  
  149.         # if the workgroup was trivial
  150.         else
  151.             lenhomlist := Length(homlist);
  152.             workgroup := Kernel(homlist[lenhomlist]);
  153.  
  154.             # if auxiliary[lenhmlist] is bounded, it is faster to augment it
  155.             # by generators of Kernel(homlist[lenhomlist])
  156.             if IsBound(auxiliary[lenhomlist])  then
  157.                 workgroup := auxiliary[lenhomlist];
  158.                 for g in Kernel(homlist[lenhomlist]).generators do
  159.                     workgroup := Closure(workgroup,g);
  160.                 od;
  161.             fi;
  162.             Unbind(auxiliary[lenhomlist]);
  163.             Unbind(homlist[lenhomlist]);
  164.  
  165.         fi; # workgroup is nontrivial-trivial
  166.     od;
  167.  
  168.     # make the modified operations record
  169.     ops := Copy( PermGroupOps );
  170.     ops.FactorGroup := function ( G, F )
  171.         if G.cspgNormal = F  then
  172.             return G.cspgFactor;
  173.         else
  174.             return PermGroupOps.FactorGroup( G, F );
  175.         fi;
  176.     end;
  177.  
  178.     # loop over the subgroups
  179.     list := [];
  180.     s := Subgroup( Gr, normals[1] );
  181.     s.size := Size( Gr );
  182.     s.operations := ops;
  183.     for i  in [2..Length(normals)]  do
  184.         t := Subgroup( Gr, normals[i] );
  185.         t.size := s.size / factorsize[i-1];
  186.         s.cspgNormal := t;
  187.         s.cspgFactor := Group( factors[i-1], () );
  188.         s.cspgFactor.size := factorsize[i-1];
  189.         s.cspgFactor.factorNum := s;
  190.         s.cspgFactor.factorDen := t;
  191.         s.cspgFactor.naturalHomomorphism := GroupHomomorphismByImages(
  192.                 s, s.cspgFactor, normals[i-1], factors[i-1] );
  193.         Add( list, s );
  194.         s := t;
  195.         s.operations := ops;
  196.     od;
  197.     t := TrivialSubgroup( Gr );
  198.     if s.size / factorsize[Length(normals)] <> 1  then
  199.         Error("this shouldn't happen");
  200.     fi;
  201.     s.cspgNormal := t;
  202.     s.cspgFactor := Group( factors[Length(normals)], () );
  203.     s.cspgFactor.size := factorsize[Length(normals)];
  204.     s.cspgFactor.factorNum := s;
  205.     s.cspgFactor.factorDen := t;
  206.     s.cspgFactor.naturalHomomorphism := GroupHomomorphismByImages(
  207.        s, s.cspgFactor, normals[Length(normals)], factors[Length(normals)] );
  208.     Add( list, s );
  209.     Add( list, t );
  210.         
  211.     # return output
  212.     return list;
  213. end;
  214.  
  215.  
  216. #############################################################################
  217. ##
  218. #F  NonPerfectCSPG()  . . . . . . . .  non perfect case of composition series
  219. ##
  220. ##  When <workgroup> is not perfect, it fills up the factor group of the
  221. ##  commutator subgroup with cyclic factors.
  222. ##  Output is the first index in normals which remains undefined
  223. ##
  224. NonPerfectCSPG := function( homlist, normals, factors, auxiliary,
  225.                             factorsize, top, index, D, workgroup )
  226.     local   listlength,   # number of cyclic factors to add to factors
  227.             indexup,      # loop variable for adding the cyclic factors
  228.             oldworkup,    # loop subgroups between
  229.             workup,       # workgroup and derived subgrp
  230.             order,        # index of oldworkup in workup
  231.             orderlist,    # prime factors of order
  232.             g, p,         # generators of workup, oldworkup
  233.             h,            # a power of g
  234.             i, j;         # loop variables
  235.  
  236.     # number of primes in factor <workgroup> / <derived subgroup>
  237.     listlength := Length(FactorsInt(top));
  238.     indexup := index+listlength;
  239.     oldworkup := D;
  240.  
  241.     # starting with the derived subgroup, add generators g of workgroup
  242.     # each addition produces a cyclic factor group on top of previous;
  243.     # appropriate powers of g will divide the cyclic factor group into
  244.     # factors of prime length
  245.     for g in workgroup.generators do
  246.         if not (g in oldworkup)  then
  247.             workup := Closure(oldworkup, g);
  248.             order := Size(workup)/Size(oldworkup);
  249.             orderlist := FactorsInt(order);
  250.             for i in [1..Length(orderlist)] do
  251.  
  252.                 # h is the power of g which adds prime length factors
  253.                 h := g^Product([i+1..Length(orderlist)],x->orderlist[x]);
  254.  
  255.                 # construct entries in factors, normals
  256.                 factors[indexup -1] := [];
  257.                 normals[indexup -1] := [];
  258.                 for p in oldworkup.generators do
  259.  
  260.                     # p acts trivially in factor group
  261.                     Add(factors[indexup -1],());
  262.  
  263.                     # preimage of p is a generator in normals
  264.                     Add(normals[indexup -1],PullbackCSPG(p,homlist));
  265.  
  266.                 od;
  267.  
  268.                 # workgroup is a factor group of original input;
  269.                 # kernel of homomorphism must be added to gens in normals
  270.                 PullbackKernelCSPG(homlist,normals,factors,
  271.                                    auxiliary,indexup-1);
  272.  
  273.                 # add preimage of h to generator list
  274.                 Add(normals[indexup-1],PullbackCSPG(h,homlist));
  275.  
  276.                 # add a prime length cycle to factor group action
  277.                 Add(factors[indexup-1],
  278.                            PermList(Concatenation([2..orderlist[i]],[1])));
  279.                 # size of factor group is a prime
  280.  
  281.                 factorsize[indexup-1] := orderlist[i];
  282.                 indexup := indexup -1;
  283.  
  284.             od;
  285.             oldworkup := workup;
  286.         fi;
  287.     od;
  288.  
  289.     return index+listlength;
  290. end;
  291.  
  292.  
  293. #############################################################################
  294. ##
  295. #F  PerfectCSPG() . . . . . . . . . . . .  prefect case of composition series
  296. ##
  297. ##  Computes maximal normal subgroup of perfect primitive group K and adds
  298. ##  its factor group to factors.
  299. ##  Output is the maximal normal subgroup NN. In case NN=1 (i.e. K simple),
  300. ##  the kernel of homomorphism which produced K is returned
  301. ##
  302. PerfectCSPG := function( homlist, normals, factors, auxiliary,
  303.                          factorsize, index, K )
  304.     local   whichcase,  # var indicating to which case of the O'Nan-Scott
  305.                         # theorem K belongs. When Size(K) and degree do not
  306.                         # determine the case without ambiguity, whichcase
  307.                         # has value as in case of unique nonregular
  308.                         # minimal normal subgroup
  309.             N,          # normal subgroup of K
  310.             H,          # subgroup of K in nontrivial centralizer case
  311.             g,          # generator of subgroups
  312.             lenhomlist, # length of homlist
  313.             kernel,     # output
  314.             ready;      # boolean variable indicating whether normal subgroup
  315.                         # was found
  316.  
  317.     while not IsSimple(K)  do
  318.         whichcase := CasesCSPG(K);
  319.  
  320.         # becomes true if we find proper normal subgroup by first methof
  321.         ready := false;
  322.  
  323.         # whichcase[1] is true in nonregular minimal normal subgroup case
  324.         if whichcase[1]  then
  325.             N := FindNormalCSPG(K, whichcase);
  326.  
  327.             # check size of result to terminate fast in ambiguous cases
  328.             if 1 < Size(N)  and Size(N) < Size(K)  then
  329.                 # K is a factor group with N in the kernel
  330.                 K := NinKernelCSPG(K,N,homlist,auxiliary);
  331.                 K.derivedSubgroup := K;
  332.                 ready := true;
  333.             fi;
  334.  
  335.        fi;
  336.  
  337.         # apply regular normal subgroup with nontrivial centralizer method
  338.         if not (ready)  then
  339.             H := NormalizerStabCSPG(K);
  340.             H := TransStabCSPG(K,H);
  341.             K := RegularNinKernelCSPG(K,H,homlist);
  342.             K.derivedSubgroup := K;
  343.         fi;
  344.  
  345.     od;
  346.  
  347.     # add next entry to the CompositionSeries output lists
  348.     factors[index] := [];
  349.     normals[index] := [];
  350.     factorsize[index] := Size(K);
  351.     for g in K.generators do
  352.         Add(factors[index],g); # store generators for image
  353.         Add(normals[index],PullbackCSPG(g,homlist));
  354.     od;
  355.  
  356.     # add generators for kernel to normals
  357.     PullbackKernelCSPG(homlist,normals,factors,auxiliary,index);
  358.     lenhomlist := Length(homlist);
  359.  
  360.     # determine output of routine
  361.     if lenhomlist > 0  then
  362.         kernel := Kernel(homlist[lenhomlist]);
  363.         if IsBound(auxiliary[lenhomlist])  then
  364.             kernel := auxiliary[lenhomlist]; # faster to add this way
  365.             for g in Kernel(homlist[lenhomlist]).generators do
  366.                 kernel := Closure(kernel,g);
  367.             od;
  368.         fi;
  369.         Unbind(homlist[lenhomlist]);
  370.         Unbind(auxiliary[lenhomlist]);
  371.  
  372.     # case when we found last factor of original group
  373.     else
  374.         kernel := Group(());
  375.     fi;
  376.  
  377.     return kernel;
  378. end;
  379.  
  380.  
  381. #############################################################################
  382. ##
  383. #F  CasesCSPG() . . . . . . . . . . . . determine case of O'Nan Scott theorem
  384. ##
  385. ##  Input: primitive, perfect, nonsimple group G.
  386. ##  CasesCSPG determines whether there is a normal subgroup with
  387. ##  nontrivial centralizer (output[1] := false) or decomposes the
  388. ##  degree of G into the form output[2]^output[3], output[1] := true (case
  389. ##  of nonregular minimal normal subgroup).
  390. ##  There are some ambiguous cases, (e.g. degree=2^15) when Size(G)
  391. ##  and degree do not determine which case G belongs to. In these cases,
  392. ##  the output is as in case of nonregular minimal normal subgroup.
  393. ##  This computation duplicates some of what is done in IsSimple.
  394. ##
  395. CasesCSPG := function(G)
  396.     local   degree,     # degree of G
  397.             g,          # order of G
  398.             primes,     # list of primes in prime decomposition of degree
  399.             output,     # output of routine
  400.             n,m,o,p,i,  # loop variables
  401.             tab1,       # table of orders of primitive groups
  402.             tab2,       # table of orders of perfect transitive groups
  403.             base;       # prime occuring in order of outer automorphism
  404.                         # group of some group in tab1
  405.  
  406.     g := Size(G);
  407.     degree := PermGroupOps.LargestMovedPoint(G);
  408.     output := [];
  409.  
  410.     # case of two regular normal subgroups
  411.     if Size(G)=degree^2  then
  412.         output[1] := false;
  413.         return output;
  414.     fi;
  415.  
  416.     # degree is not prime power
  417.     primes := FactorsInt(degree);
  418.     if primes[1] < primes[Length(primes)] then
  419.         output[1] := true;
  420.         # only case when index of primitive group in socle is not 2*prime
  421.         if Length(primes)=15  then
  422.             output[2] := 12;
  423.             output[3] := 5;
  424.         else
  425.             output[2] := primes[1]*primes[Length(primes)];
  426.             output[3] := Length(primes)/2;
  427.         fi;
  428.         return output;
  429.  
  430.     # in case of prime power degree, we have to determine the possible
  431.     # orders of G with nonabelian socle. See IsSimple for identification
  432.     # of groups in tab1,tab2
  433.     else
  434.         tab1 := [ ,,,,[60],,[168,2520],[168,20160],[504,181440],,
  435.                   [660,7920,19958400],,[5616,3113510400]];
  436.         tab2 := [ ,,,,[60],[60,360],[168,2520],[168,8168,20160]];
  437.         for n in [5,7,8,9,11,13] do
  438.             for m in [5..8] do
  439.                 for o in [1..Length(tab1[n])] do
  440.                     for p in [1..Length(tab2[m])] do
  441.                         if tab1[n][o]=504  then
  442.                             base := 3;
  443.                         else
  444.                             base := 2;
  445.                         fi;
  446.                         if degree=n^m
  447.                           and g mod (tab1[n][o]^m*tab2[m][p]) = 0
  448.                           and (tab1[n][o]^m*tab2[m][p]*base^m) mod g = 0
  449.                         then
  450.                             output[1] := true;
  451.                             output[2] := n;
  452.                             output[3] := m;
  453.                             return output;
  454.                         fi;
  455.                     od;
  456.                 od;
  457.             od;
  458.         od;
  459.  
  460.         # if the order of G did not satisfy any of the nonabelian socle
  461.         # possibilities, output the abelian socle message
  462.         output[1] := false;
  463.         return output;
  464.  
  465.     fi;
  466.  
  467. end;
  468.  
  469.  
  470. #############################################################################
  471. ##
  472. #F  FindNormalCSPG()  . . . . . . . . . . . . . find a proper normal subgroup
  473. ##
  474. ##  given perfect, primitive G with unique nonregular minimal normal
  475. ##  subgroup, the routine returns a proper normal subgroup of G
  476. ##
  477. FindNormalCSPG := function ( G, whichcase )
  478.     local   n,          # degree of G
  479.             i,          # loop variable
  480.             stabgroup,  # stabilizer subgroup of first point
  481.             orbits,     # list of orbits of stabgroup
  482.             where,      # index of shortest orbit in orbits
  483.             len,        # length of shortest orbit
  484.             tchom,      # trans. constituent homom. of stabgroup
  485.                         # to shortest orbit
  486.             bl,         # blocks in action of stabgroup on shortest orbit
  487.             bhom,       # block homomorphism for the action on bl
  488.             K,          # homomorph image of stabgroup at tchom, bhom
  489.             kernel,     # kernel of bhom
  490.             N;          # output; normal subgroup of G
  491.  
  492.     # whichcase[1] is true if G has no normal subgroup with nontrivial
  493.     # centralizer or we cannot determine this fact from Size(G)
  494.     n := PermGroupOps.LargestMovedPoint(G);
  495.     stabgroup := Stabilizer(G,G.orbit[1],OnPoints);
  496.     orbits := Orbits(stabgroup,[1..n]);
  497.  
  498.     # find shortest orbit of stabgroup
  499.     len := n; where := 1;
  500.     for i in [1..Length(orbits)] do
  501.         if (1<Length(orbits[i])) and (Length(orbits[i])< len)  then
  502.             where := i;
  503.             len := Length(orbits[i]);
  504.         fi;
  505.     od;
  506.  
  507.     # check arith. conditions in order to terminate fast in ambiguous cases
  508.     if len mod whichcase[3] = 0 and len <= whichcase[3]*(whichcase[2]-1) then
  509.  
  510.         # take action of stabgroup on shortest orbit
  511.         tchom := OperationHomomorphism(stabgroup,
  512.                              Operation(stabgroup,orbits[where]));
  513.         K := Image(tchom,stabgroup);
  514.         bl := MaximalBlocks(K,[1..len]);
  515.  
  516.         # take action on blocks
  517.         if Length(bl) > 1  then
  518.             bhom := OperationHomomorphism(K,Operation(K,bl,OnSets));
  519.             K := Image(bhom,K);
  520.             kernel := Kernel(CompositionMapping(bhom,tchom));
  521.             N := NormalClosure(G,kernel);
  522.  
  523.             # another check for ambiguous cases
  524.             if Size(N) < Size(G) then
  525.                 return N;
  526.             fi;
  527.  
  528.         fi;
  529.     fi;
  530.  
  531.     # in ambiguous case, return trivial subgroup
  532.     N := Subgroup(Parent(G),[()]);
  533.     return N;
  534. end;
  535.  
  536.  
  537. #############################################################################
  538. ##
  539. #F  NinKernelCSPG() . . . . . find homomorphism that contains N in the kernel
  540. ##
  541. ##  Given a normal subgroup N of G, creates a subgroup H such that the
  542. ##  homomorphism to the action on cosets of H contains N in the kernel.
  543. ##  Actually, only the image of a subgroup is computed, and we store
  544. ##  N in auxiliary to remember that N should be added to kernel of
  545. ##  homomorphism.
  546. ##  Output is the image at homomorphism
  547. ##
  548. NinKernelCSPG := function ( G, N, homlist, auxiliary )
  549.     local   i,j,        # loop variables
  550.             base,       # base of G
  551.             H,HOld,     # subgroups of G
  552.             G1,H1,      # stabilizer chains of G, HOld
  553.             g,          # loop variable: generator of subgroups of G
  554.             block,      # set of cosets of G1[i]; G1[i] is represented on
  555.                         # images of block
  556.             newrep,     # blocks of imprimitivity in
  557.             oldnewrep,  # the representation of G1[i]
  558.             bhom,       # block hom. and
  559.             tchom;      # transisitive const. hom. applied to G1[i]
  560.  
  561.     j := Length(homlist)+1;
  562.     auxiliary[j] := N;
  563.  
  564.     # find smallest subgroup of G in stabilizer chain which, together with N,
  565.     # generates G
  566.     base := Base(G);
  567.     G1 := ListStabChain(G);
  568.     i := Length(base)+1;
  569.     H := N;
  570.     repeat
  571.         HOld := H;
  572.         i := i-1;
  573.         for g in G1[i].generators do
  574.             H := Closure(H,g);
  575.         od;
  576.     until Size(H) = Size(G);
  577.  
  578.     # represent G1[i] on cosets of H1[i] := G1[i+1]N \cap G1[i]
  579.     MakeStabChain(HOld,base);
  580.     ExtendStabChain(HOld,base);
  581.     H1 := ListStabChain(HOld);
  582.  
  583.     # G1[i] will be represented on images of block
  584.     block := Set( H1[i].orbit );
  585.     for j in [1..i-1] do
  586.         G := Stabilizer(G,G.orbit[1],OnPoints);
  587.     od;
  588.  
  589.     # now G is the previous G1[i]
  590.     tchom := OperationHomomorphism(G,Operation(G,G.orbit));
  591.     G := Image(tchom,G);
  592.  
  593.     # image of block at trans. const. homom.
  594.     block := OnSets(block,tchom.conperm);
  595.  
  596.     # find primitive action on images of block
  597.     newrep := Orbit( G, block, OnSets );
  598.     while Length(newrep) > 1 do
  599.         oldnewrep := newrep;
  600.         newrep := Blocks(G,newrep,OnSets);
  601.         newrep := List(newrep,Union);
  602.     od;
  603.     if Length(oldnewrep) < Length(G.orbit)  then
  604.         bhom := OperationHomomorphism(G,Operation(G,oldnewrep,OnSets));
  605.         Add(homlist,CompositionMapping(bhom,tchom));
  606.         G := Image(bhom,G);
  607.     else
  608.         Add(homlist,tchom);
  609.     fi;
  610.  
  611.     return G;
  612. end;
  613.  
  614.  
  615. #############################################################################
  616. ##
  617. #F  RegularNinKernelCSPG()  . . . .  action of G on H and on maximal subgroup
  618. ##
  619. ##  H is transitive and contains the stabilizer of the first two
  620. ##  base points; we want to find the action of G on cosets of H, and
  621. ##  then the action of G on cosets of a maximal subgroup K containing H
  622. ##  reference: Beals-Seress Lemma 4.3.
  623. ##
  624. RegularNinKernelCSPG := function ( G, H, homlist )
  625.     local   i,j,k,      # loop variables
  626.             base,       # base of G
  627.             G1,H1,      # stabilizer chain of G,H
  628.             x,y,        # first two base points of G
  629.             stabgroup,  # stabilizer of x in G
  630.             Ginverses,  # list of inverses of generators of G
  631.             Hinverses,  # list of inverses of generators of H
  632.             stabinverses, # list of inverses of generators of stabgroup
  633.             block,      # orbit of y in H_x
  634.             orbits,     # images of block in G_x=stabgroup
  635.             a,          # cardinality of orbits
  636.             b,          # cardinality of block
  637.             reprlist,   # for z in stabgroup.orbit, reprlist[z] tells which
  638.                         # element of orbits z belongs to
  639.             reps,       # for representatives z of sets in orbits, reps
  640.                         # contains the cosetrep carrying z to y in stabgroup
  641.                         # (as a word in generators of stabgroup)
  642.             inversereps,# the inverses of words in reps
  643.                         # (as words in stabinverses)
  644.             images,     # list containing the images of generators of G,
  645.                         # acting on cosets of H (there are $a$ cosets,
  646.                         # represented by the elements of orbits)
  647.             v,          # point of permutation domain
  648.             tau,        # the cosetrep of H carrying v to x
  649.                         # (as a word in H.gen's)
  650.             tauinverse, # the inverse of tau (as a word in Hinverses)
  651.             word,       # list of permutations coding a cosetrep of H
  652.             K,          # the factor group of G generated by images
  653.             newrep,     # block system from cosets of K
  654.             c,          # cardinality of newrep
  655.             d,          # size of one block
  656.             newimages,  # list containing the action of generators of G, on
  657.                         # newrep
  658.             hom;        # the homomorphism G->K
  659.  
  660.     base := Base(G);
  661.     G1 := ListStabChain(G);
  662.     ExtendStabChain(H,base);
  663.     H1 := ListStabChain(H);
  664.     block := Set( H1[2].orbit );
  665.     stabgroup := Stabilizer(G,G.orbit[1],OnPoints);
  666.     x := G.orbit[1];
  667.     y := stabgroup.orbit[1];
  668.     orbits := Orbit(stabgroup,block,OnSets);
  669.     a := Length(orbits);
  670.     b := Length(block);
  671.     reprlist := [];
  672.     for i in [1..a] do
  673.         for k in [1..b] do
  674.             reprlist[orbits[i][k]] := i;
  675.         od;
  676.     od;
  677.  
  678.     Ginverses := [];
  679.     for i in [1..Length(G.generators)] do
  680.         Ginverses[i] := G.generators[i]^(-1);
  681.     od;
  682.  
  683.     Hinverses := [];
  684.     for i in [1..Length(H.generators)] do
  685.         Hinverses[i] := H.generators[i]^(-1);
  686.     od;
  687.     Add(H.generators,());
  688.     Add(Hinverses,());
  689.  
  690.     stabinverses := [];
  691.     for i in [1..Length(stabgroup.generators)] do
  692.         stabinverses[i] := stabgroup.generators[i]^(-1);
  693.     od;
  694.     Add(stabgroup.generators,());
  695.     Add(stabinverses,());
  696.  
  697.     reps := []; inversereps := [];
  698.     for i in [1..a] do
  699.         reps[i] := CosetRepAsWord(y,orbits[i][1],stabgroup.transversal);
  700.         inversereps[i] := InverseAsWord(reps[i],stabgroup.generators,
  701.                                                 stabinverses);
  702.     od;
  703.  
  704.     # construct action of G-generators on cosets of H. Each coset of H has a
  705.     # representative in orbits; to find the image of an H coset
  706.     # at multiplication by G.generators[i], take element of H coset such that
  707.     # the product with G.generators[i] fixes x. Then the image of the coset
  708.     # can be read from the position in orbits (cf. Lemma 4.3)
  709.     images := [];
  710.     for i in [1..Length(G.generators)] do
  711.         images[i] := [];
  712.         for j in [1..a] do
  713.             v := ImageInWord(x,Concatenation([Ginverses[i]],reps[j]));
  714.             tau := CosetRepAsWord(x,v,H.transversal);
  715.             tauinverse := InverseAsWord(tau,H.generators,Hinverses);
  716.             word := Concatenation(tauinverse,inversereps[j],
  717.                                   [G.generators[i]]);
  718.             images[i][j] := reprlist[ImageInWord(y,word)];
  719.         od;
  720.         images[i] := PermList(images[i]);
  721.     od;
  722.     Unbind(H.generators[Length(H.generators)]);
  723.     K := Group(images,());
  724.  
  725.     # check whether new representation is primitive. If not, construct action
  726.     # on maximal block system
  727.     newrep := MaximalBlocks(K,[1..a]);
  728.     if Length(newrep) > 1  then
  729.         c := Length(newrep);
  730.         d := Length(newrep[1]);
  731.         reprlist := [];
  732.         for i in [1..c] do
  733.             for k in [1..d] do
  734.                 reprlist[newrep[i][k]] := i;
  735.             od;
  736.         od;
  737.         newimages := [];
  738.         for i in [1..Length(G.generators)] do
  739.             newimages[i] := [];
  740.             for k in [1..c] do
  741.                 newimages[i][k] := reprlist[newrep[k][1]^images[i]];
  742.             od;
  743.             newimages[i] := PermList(newimages[i]);
  744.         od;
  745.         K := Group(newimages,());
  746.         hom := GroupHomomorphismByImages(G,K,G.generators,newimages);
  747.         hom.isMapping := true;
  748.     else
  749.         hom := GroupHomomorphismByImages(G,K,G.generators,images);
  750.         hom.isMapping := true;
  751.     fi;
  752.     j := Length(homlist)+1;
  753.     homlist[j] := hom;
  754.     K := Image(homlist[j],G);
  755.     K.derivedSubgroup := K;
  756.  
  757.     return K;
  758. end;
  759.  
  760.  
  761. #############################################################################
  762. ##
  763. #F  NormalizerStabCSPG()  . . . . . . . . .  normalizer of 2 point stabilizer
  764. ##
  765. ##  given primitive, perfect group which has regular normal subgroup
  766. ##  with nontrivial centralizer, the output is N_G(G_{xy})
  767. ##
  768. NormalizerStabCSPG := function(G)
  769.     local   n,          # degree of G
  770.             stabgroup,  # stabilizer group of G
  771.             orbits,     # orbits of stabgroup
  772.             len,        # minimal length of stabgroup orbits
  773.             where,      # index of minimal length orbit
  774.             i,          # loop variable
  775.             base,       # base of G
  776.             stabgroup2, # stabilizer of first two base points in G
  777.             x,y,        # first two base points
  778.             normalizer, # output group N_G(G_{xy})
  779.             L,          # fixed points of stabgroup2
  780.             yL,         # intersection of L and y-orbit in stabgroup
  781.             orbity,     # orbit of y in normalizer_x;
  782.                         # eventually, orbity must be yL
  783.             orbitx,     # orbit of x in normalizer;
  784.                         # eventually, orbitx must be L
  785.             u,v,        # points in permutation domain
  786.             tau,sigma,p,# cosetreps of G, stabgroup
  787.             Ltau;       # image of L under tau
  788.  
  789.     n := PermGroupOps.LargestMovedPoint(G);
  790.     stabgroup := Stabilizer(G,G.orbit[1],OnPoints);
  791.  
  792.     # If necessary, make base change to achieve that second base point is
  793.     # in smallest orbit of stabilizer.
  794.     orbits := Orbits(stabgroup,[1..n]);
  795.     len := n; where := 1;
  796.     for i in [1..Length(orbits)] do
  797.         if (1<Length(orbits[i])) and (Length(orbits[i])< len)  then
  798.             where := i;
  799.             len := Length(orbits[i]);
  800.         fi;
  801.     od;
  802.     if Length(stabgroup.orbit) > len  then
  803.         MakeStabChain(G,[G.orbit[1],orbits[where][1]]);
  804.         stabgroup := Stabilizer(G,G.orbit[1],OnPoints);
  805.     fi;
  806.     base := Base(G);
  807.     x := G.orbit[1];
  808.     y := stabgroup.orbit[1];
  809.     stabgroup2 := Stabilizer(stabgroup,y,OnPoints);
  810.  
  811.     # compute normalizer. Method: Beals-Seress, Lemma 7.1
  812.     L := Difference([1..n],PermGroupOps.MovedPoints(stabgroup2));
  813.     yL := Intersection(L,stabgroup.orbit);
  814.  
  815.     # initialize normalizer to G_{xy}
  816.     normalizer := Subgroup(Parent(G),
  817.                            PermGroupOps.StrongGenerators(stabgroup2));
  818.     orbity := Orbit(normalizer,y);
  819.     while Length(orbity) < Length(yL) do
  820.         v := Difference(yL,orbity)[1];
  821.         p := Product(CosetRepAsWord(y,v,stabgroup.transversal));
  822.         Add(normalizer.generators,p);
  823.         orbity := Orbit(normalizer,y);
  824.     od;
  825.     orbitx := Orbit(normalizer,x);
  826.     while Length(orbitx) < Length(L) do
  827.         v := Difference(L,orbitx)[1];
  828.         tau := Product(CosetRepAsWord(x,v,G.transversal));
  829.         Ltau := OnSets(L,tau);
  830.         u := Intersection(Ltau,stabgroup.orbit)[1];
  831.         sigma := Product(CosetRepAsWord(y,u,stabgroup.transversal));
  832.         Add(normalizer.generators,tau*sigma);
  833.         orbitx := Orbit(normalizer,x);
  834.     od;
  835.     MakeStabChainStrongGenerators(normalizer,base,normalizer.generators);
  836.     ExtendStabChain(normalizer,base);
  837.  
  838.     return normalizer;
  839. end;
  840.  
  841.  
  842. #############################################################################
  843. ##
  844. #F  TransStabCSPG() . . . embed a 2 point stabilizer in a transitive subgroup
  845. ##
  846. ##  given a subgroup H of G which contains G_{xy}, the stabilizer of the
  847. ##  first two points in G, and a theoretical guarantee that there is a
  848. ##  proper transitive subgroup K containing H, the routine finds such K
  849. ##
  850. TransStabCSPG := function(G,H)
  851.     local   n,          # degree of G
  852.             x,y,        # first two points of the base of G
  853.             stabgroup,  # stabilizer of x in G
  854.             hstabgroup, # stabilizer of x in H
  855.             u,v,        # indices of points in G.orbit, stabgroup.orbit
  856.             g,          # list of permutations whose product is
  857.                         # (semi)random element of G
  858.             notinH,     # boolean; true if g is not in H
  859.             word,       # list of permutations whose product is
  860.                         # (semi)random element of <H,g>
  861.             tau,sigma,  # lists of permutations whose
  862.             tau1,sigma1,# products are coset representatives
  863.             i,j,        # loop variables
  864.             K;          # K=<H,g>
  865.  
  866.     #Print(Size(G),",",Size(H));
  867.     n := PermGroupOps.LargestMovedPoint(G);
  868.     x := G.orbit[1];
  869.     stabgroup := Stabilizer(G,x,OnPoints);
  870.     y := stabgroup.orbit[1];
  871.     hstabgroup := Stabilizer(H,x,OnPoints);
  872.     ExtendStabChain(H,Base(G));
  873.     ExtendStabChain(hstabgroup,Base(stabgroup));
  874.  
  875.     # try to embed H into bigger subgroups; stop when result is transitive
  876.     repeat
  877.         #Print("brum");
  878.  
  879.         # first, take random element of G\H
  880.         repeat
  881.             v := Random([1..Length(G.orbit)]);
  882.             g := CosetRepAsWord(x,G.orbit[v],G.transversal);
  883.             u := Random([1..Length(stabgroup.orbit)]);
  884.             Append(g,CosetRepAsWord(y,stabgroup.orbit[u],
  885.                                       stabgroup.transversal));
  886.             notinH := false;
  887.             v := ImageInWord(x,g);
  888.             if not IsBound(H.transversal[v])  then
  889.                 notinH := true;
  890.             else
  891.                 u := ImageInWord(y,Concatenation(g,
  892.                                         CosetRepAsWord(x,v,H.transversal)));
  893.                 if not IsBound(hstabgroup.transversal[u])  then
  894.                     notinH := true;
  895.                 fi;
  896.             fi;
  897.         until  notinH;
  898.  
  899.         for i in [1..n] do
  900.  
  901.             # construct semirandom element of <H,g>
  902.             word := [];
  903.             for j in [1..5] do
  904.                 Append(word,g);
  905.                 Append(word,RandomElmAsWord(H));
  906.             od;
  907.  
  908.             # check whether word is in H;
  909.             # if not, then let g=cosetrep of word in G_{xy}
  910.             v := ImageInWord(x,word);
  911.             tau := CosetRepAsWord(x,v,H.transversal);
  912.             if tau = []  then
  913.                 tau1 := CosetRepAsWord(x,v,G.transversal);
  914.                 u := ImageInWord(y,Concatenation(word,tau1));
  915.                 sigma1 := CosetRepAsWord(y,u,stabgroup.transversal);
  916.                 g := Concatenation(tau1,sigma1);
  917.             else
  918.                 u := ImageInWord(y,Concatenation(word,tau));
  919.                 sigma := CosetRepAsWord(y,u,hstabgroup.transversal);
  920.                 if sigma = []  then
  921.                     tau1 := CosetRepAsWord(x,v,G.transversal);
  922.                     u := ImageInWord(y,Concatenation(word,tau1));
  923.                     sigma1 := CosetRepAsWord(y,u,stabgroup.transversal);
  924.                     g := Concatenation(tau1,sigma1);
  925.                 fi;
  926.             fi;
  927.         od;
  928.  
  929.         # check whether H,g generate a proper subgroup of G
  930.         K := Closure(H,Product(g));
  931.         if 1 < Size(G)/Size(K)  then
  932.             H := K;
  933.             #Print(Size(H));
  934.             hstabgroup := Stabilizer(H,x,OnPoints);
  935.             ExtendStabChain(hstabgroup,Base(stabgroup));
  936.             ExtendStabChain(H,Base(G));
  937.         fi;
  938.  
  939.     until Length(H.orbit) = n;
  940.  
  941.     return H;
  942. end;
  943.  
  944.  
  945. #############################################################################
  946. ##
  947. #F  PullbackKernelCSPG()  . . . . . . . . . . . . . . . pull back the kernels
  948. ##
  949. PullbackKernelCSPG := function( homlist, normals, factors, auxiliary, index )
  950.     local   lenhomlist, # length of homlist
  951.             i, j,       # loop variables
  952.             gens,       # list of generators in kernels
  953.                         # of homomorphisms in homlist
  954.             g;          # a member of gens
  955.  
  956.     # for each kernel, compute preimages of the kernel generators in the
  957.     # input group add these to generators of the current subnormal subgroup
  958.     # in the composition series
  959.     lenhomlist := Length(homlist);
  960.     for i in [1..lenhomlist] do
  961.        if IsBound(auxiliary[i])  then
  962.            gens := Union(Kernel(homlist[i]).generators,
  963.                          auxiliary[i].generators);
  964.        else
  965.            gens := Kernel(homlist[i]).generators;
  966.        fi;
  967.        for g in gens do
  968.            for j in [1..i-1] do
  969.                g := PreImagesRepresentative(homlist[i-j],g);
  970.            od;
  971.            Add(normals[index],g);
  972.            Add(factors[index],());
  973.        od;
  974.     od;
  975. end;
  976.  
  977.  
  978. #############################################################################
  979. ##
  980. #F  PullbackCSPG()  . . . . . . . . . . . . . . . . . . . . . . . . pull back
  981. ##
  982. PullbackCSPG := function(p,homlist)
  983.     local   i,          # loop variable
  984.             lenhomlist; # length of homlist
  985.  
  986.     # compute a preimage of the permutation p in the input group
  987.     lenhomlist := Length(homlist);
  988.     for i in [1..lenhomlist] do
  989.         p := PreImagesRepresentative(homlist[lenhomlist+1-i],p);
  990.     od;
  991.     return p;
  992. end;
  993.  
  994.  
  995. #############################################################################
  996. ##
  997. #F  CosetRepAsWord()  . . . . . . . . .  write a coset representative as word
  998. ##
  999. ##  returns the cosetrep carrying y to the base point x as a word in the
  1000. ##  generators. If y is not in the orbit of x, returns []
  1001. ##
  1002. CosetRepAsWord := function(x,y,transversal)
  1003.     local   word,       # list of permutations
  1004.             point;      # element of permutation domain
  1005.  
  1006.     word := [];
  1007.     if IsBound(transversal[y])  then
  1008.         point := y;
  1009.         repeat
  1010.             Add(word,transversal[point]);
  1011.             point := point^transversal[point];
  1012.         until point = x;
  1013.     fi;
  1014.     return word;
  1015. end;
  1016.  
  1017.  
  1018. #############################################################################
  1019. ##
  1020. #F  ImageInWord() . . .  image of a point under a permutation written as word
  1021. ##
  1022. ##  computes the image of x when the list of permutations word is applied
  1023. ##
  1024. ImageInWord := function(x,word)
  1025.     local   i,          # loop variable
  1026.             value;      # element of permutation domain
  1027.  
  1028.     value := x;
  1029.     for i in [1..Length(word)] do
  1030.         value := value^word[i];
  1031.     od;
  1032.     return value;
  1033. end;
  1034.  
  1035.  
  1036. #############################################################################
  1037. ##
  1038. #F  SiftAsWord()  . . . . . . . . . . . . shift a permutation written as word
  1039. ##
  1040. ##  given a list of permutations perm, the routine computes the residue
  1041. ##  at the sifting of perm through the SGS of G
  1042. ##  the output is a list of length 2: the first component is the siftee,
  1043. ##  as a word, the second component is 0 if perm in G, and i if the siftee
  1044. ##  on the i^th level could not be computed.
  1045. ##
  1046. SiftAsWord := function(G,perm)
  1047.     local   i,          # loop variable
  1048.             y,          # element of permutation domain
  1049.             word,       # the list collecting the siftee of perm
  1050.             index,      # the level where the siftee cannot be computed
  1051.             G1;         # stabilizer subgroup chain of G
  1052.  
  1053.     # perm must be a list of permutations itself!
  1054.     G1 := ListStabChain(G);
  1055.     word := perm;
  1056.     index := 0;
  1057.     for i in [1..Length(G1)-1] do
  1058.         y := ImageInWord(G1[i].orbit[1],word);
  1059.         if IsBound(G1[i].transversal[y])  then
  1060.             Append(word, CosetRepAsWord(G1[i].orbit[1],y,G1[i].transversal));
  1061.         else
  1062.             index := i;
  1063.             return [word,index];
  1064.         fi;
  1065.     od;
  1066.  
  1067.     return [word,index];
  1068. end;
  1069.  
  1070.  
  1071. #############################################################################
  1072. ##
  1073. #F  InverseAsWord() . . . . . . . . . .  invert a permutation written as word
  1074. ##
  1075. ##  given a list of permutations "list", the inverses of these permutations
  1076. ##  in inverselist, and a list of permutations "word" with elements from
  1077. ##  list, returns the inverse of word as a list of inverses from inverselist
  1078. ##
  1079. InverseAsWord := function(word,list,inverselist)
  1080.     local   i,          # loop variable
  1081.             inverse;    # the inverse of word
  1082.  
  1083.     inverse := [];
  1084.     for i in [1..Length(word)] do
  1085.         inverse[i] := inverselist[Position(list,word[Length(word)+1-i])];
  1086.     od;
  1087.     return inverse;
  1088. end;
  1089.  
  1090.  
  1091. #############################################################################
  1092. ##
  1093. #F  RandomElmAsWord() . . . . . . . . . . . .  random element written as word
  1094. ##
  1095. ##  given an SGS for G, returns a uniformly distributed random element of G
  1096. ##  as a word in the strong generators
  1097. ##
  1098. RandomElmAsWord := function(G)
  1099.     local   i,          # loop variable
  1100.             word,       # the random element
  1101.             G1,         # list of subgroups in stabilizer chain
  1102.             v;          # random element of orbits of groups in G1
  1103.  
  1104.     G1 := ListStabChain(G);
  1105.     word := [];
  1106.     for i in [1..Length(G1)-1] do
  1107.         v := Random([1..Length(G1[i].orbit)]);
  1108.         Append(word,CosetRepAsWord(G1[i].orbit[1],G1[i].orbit[v],
  1109.                                    G1[i].transversal));
  1110.     od;
  1111.     return word;
  1112. end;
  1113.  
  1114.  
  1115. #############################################################################
  1116. ##
  1117. #F  PCore() . . . . . . . . . . . . . . . . . . p core of a permutation group
  1118. ##
  1119. ##  O_p(G), the p-core of G, is the maximal normal p-subgroup
  1120. ##  Output of routine: the subgroup O_p(workgroup)
  1121. ##  reference: Luks-Seress
  1122. ##
  1123. PermGroupOps.PCore := function(workgroup,p)
  1124.     local   n,          # degree of workgroup
  1125.             G,          # a factor group of workgroup
  1126.             list,       # the record workgroup.compositionSeries
  1127.             normals,    # gens for the subgroups in the composition series
  1128.             factorsize, # the sizes of factor groups in composition series
  1129.             index,      # loop variable running through the indices of
  1130.                         # subgroups in the composition series
  1131.             primes,     # list of primes in the factorization of numbers
  1132.             homlist,    # list of homomorphisms applied to workgroup
  1133.             lenhomlist, # length of homlist
  1134.             K, N,       # subnormal subgroups of G from composition series
  1135.             g,          # generator of K
  1136.             C,          # centralizer of N in K
  1137.             order,      # order of a generator of C
  1138.             H,          # first solvable, then also
  1139.                         # abelian normal p-subgroup of G
  1140.             series,     # the derived series of H; H becomes abelian when it
  1141.                         # is redefined as last nontrivial term of series
  1142.             actionlist, # record of G action on transitive
  1143.                         # constituent pieces of H
  1144.             i, j,       # loop variables
  1145.             image,      # list of images of generators of G
  1146.                         # acting on pieces of H
  1147.             GG,         # the image of G at this action
  1148.             hom,        # the homomorphism from G to GG
  1149.             solvable;   # list of generators for the p-core
  1150.  
  1151.     primes := FactorsInt(p);
  1152.     if Length(primes) > 1  then
  1153.         return Subgroup(Parent(workgroup),[()]);
  1154.     fi;
  1155.     if IsTrivial(workgroup)  then
  1156.         return Subgroup(Parent(workgroup),[()]);
  1157.     fi;
  1158.     n := PermGroupOps.LargestMovedPoint(workgroup);
  1159.     G := workgroup;
  1160.     list := CompositionSeries(G);
  1161.     # normals := Copy(list[1]);
  1162.     # factorsize := list[3];
  1163.     normals := List([1..Length(list)-1],i->Copy(list[i].generators));
  1164.     factorsize := List([1..Length(list)-1],i->Size(list[i])/Size(list[i+1]));
  1165.     Add(normals, [()]);
  1166.     homlist := [];
  1167.     index := Length(factorsize);
  1168.  
  1169.     # try to find smallest subgroup in composition series with nontrivial
  1170.     # p-core. The normal closure of this p-core is a solvable normal
  1171.     # p-subgroup of G; taking commutator subgroups, find abelian normal
  1172.     # p-subgroup of G.
  1173.     # represent G acting on transitive constituent pieces of abelian normal
  1174.     # p-subgroup; kernel is abelian p-group. Take image at this action, and
  1175.     # repeat
  1176.     while index > 0 do
  1177.         if factorsize[index] <> p  then
  1178.             index := index-1;
  1179.         else
  1180.             N := Subgroup(Parent(G),normals[index+1]);
  1181.             K := N;
  1182.             for g in normals[index] do
  1183.                 K := Closure(K,g);
  1184.             od;
  1185.  
  1186.             # N has trivial p-core; check whether K has nontrivial one
  1187.             # K=N is possible when we work in homomorphic images of original
  1188.             if Size(K)=Size(N)  then
  1189.                 index := index-1;
  1190.             else
  1191.                 C := CentralizerNormalCSPG(K,N);
  1192.  
  1193.                 # O_p(K) is cyclic or trivial; it must show up in C
  1194.                 # C is always abelian; check whether it has p-part
  1195.                 for i in [1..Length(C.generators)] do
  1196.                     order := Order(C,C.generators[i]);
  1197.                     if Mod(order,p) = 0  then
  1198.                         C.generators[i] := C.generators[i]^(order/p);
  1199.                     else
  1200.                         C.generators[i] := ();
  1201.                     fi;
  1202.                 od;
  1203.  
  1204.                 # redefine C as the p-core of C
  1205.                 C := Subgroup(Parent(K),C.generators);
  1206.                 if IsTrivial(C)  then
  1207.                     index := index-1;
  1208.                 else
  1209.                     H := NormalClosure(G,C);
  1210.                     series := DerivedSeries(H);
  1211.                     H := series[Length(series)-1];
  1212.  
  1213.                     # at that moment, H is abelian normal in G
  1214.                     # define new action of G with H in the kernel
  1215.                     actionlist := ActionAbelianCSPG(H,n);
  1216.  
  1217.                     # find new action of subgroups in composition series
  1218.                     for i in [1..index] do
  1219.                         for j in [1..Length(normals[i])] do
  1220.                             normals[i][j] :=
  1221.                                 ImageOnAbelianCSPG(normals[i][j],actionlist);
  1222.                         od;
  1223.                     od;
  1224.                     image := [];
  1225.                     for i in [1..Length(G.generators)] do
  1226.                         image[i] :=
  1227.                               ImageOnAbelianCSPG(G.generators[i],actionlist);
  1228.                     od;
  1229.  
  1230.                     # take homomorphic image of G
  1231.                     GG := Group(image,());
  1232.                     hom:=GroupHomomorphismByImages(G,GG,G.generators,image);
  1233.                     Add(homlist,hom);
  1234.                     G := GG;
  1235.                     index := index-1;
  1236.  
  1237.                 fi;         # IsTrivial(C)
  1238.  
  1239.             fi;             # Size(K)=Size(N)
  1240.  
  1241.         fi;                 # factorsize[index] <> p
  1242.  
  1243.     od;
  1244.  
  1245.     # create output;
  1246.     # the p-core is the kernel of homomorphisms applied to workgroup
  1247.     lenhomlist := Length(homlist);
  1248.     if lenhomlist = 0  then
  1249.         solvable := [()];
  1250.     else
  1251.         solvable := [];
  1252.         for i in [1..lenhomlist] do
  1253.             for g in Kernel(homlist[i]).generators do
  1254.                 for j in [1..i-1] do
  1255.                     g := PreImagesRepresentative(homlist[i-j],g);
  1256.                 od;
  1257.                 Add(solvable,g);
  1258.             od;
  1259.         od;
  1260.     fi;
  1261.     return Subgroup(Parent(workgroup),solvable);
  1262. end;
  1263.  
  1264.  
  1265. #############################################################################
  1266. ##
  1267. #F  Radical() . . . . . . . . . . . . . . . .  radical of a permutation group
  1268. ##
  1269. ##  the radical is the maximal solvable normal subgroup
  1270. ##  output of routine: the subgroup radical of workgroup
  1271. ##  reference: Luks-Seress
  1272. ##
  1273. PermGroupOps.Radical := function(workgroup)
  1274.     local   n,          # degree of workgroup
  1275.             G,          # a factor group of workgroup
  1276.             list,       # the record workgroup.compositionSeries
  1277.             normals,    # gens for the subgroups in the composition series
  1278.             factorsize, # the sizes of factor groups in composition series
  1279.             index,      # loop variable running through the indices of
  1280.                         # subgroups in the composition series
  1281.             primes,     # list of primes in the factorization of numbers
  1282.             homlist,    # list of homomorphisms applied to workgroup
  1283.             lenhomlist, # length of homlist
  1284.             K, N,       # subnormal subgroups of G from composition series
  1285.             g,          # generator of K
  1286.             C,          # centralizer of N in K
  1287.             H,          # first solvable,
  1288.                         # then also abelian normal subgroup of G
  1289.             series,     # the derived series of H; H becomes abelian when it
  1290.                         # is redefined as last nontrivial term of series
  1291.             actionlist, # record of G action on transitive
  1292.                         # constituent pieces of H
  1293.             i, j,       # loop variables
  1294.             image,      # list of images of generators of G
  1295.                         # acting on pieces of H
  1296.             GG,         # the image of G at this action
  1297.             hom,        # the homomorphism from G to GG
  1298.             solvable;   # list of generators for the radical
  1299.  
  1300.     if IsTrivial(workgroup)  then
  1301.         return Subgroup(Parent(workgroup),[()]);
  1302.     fi;
  1303.  
  1304.     n := PermGroupOps.LargestMovedPoint(workgroup);
  1305.     G := workgroup;
  1306.     list := CompositionSeries(G);
  1307.     # normals := Copy(list[1]);
  1308.     # factorsize := list[3];
  1309.     normals := List([1..Length(list)-1],i->Copy(list[i].generators));
  1310.     factorsize := List([1..Length(list)-1],i->Size(list[i])/Size(list[i+1]));
  1311.     Add(normals, [()]);
  1312.     homlist := [];
  1313.     index := Length(factorsize);
  1314.  
  1315.     # try to find smallest subgroup in composition series with nontrivial
  1316.     # radical. The normal closure of this radical is a solvable normal
  1317.     # subgroup of G; taking commutator subgroups, find abelian normal
  1318.     # subgroup of G.
  1319.     # represent G acting on transitive constituent pieces of abelian normal
  1320.     # subgroup; kernel is abelian normal.
  1321.     # Take image at this action, and repeat
  1322.     while index > 0 do
  1323.         primes := FactorsInt(factorsize[index]);
  1324.  
  1325.         # if the factor group is not cyclic, no chance for nontrivial radical
  1326.         if Length(primes) > 1  then
  1327.             index := index-1;
  1328.         else
  1329.             N := Subgroup(Parent(G),normals[index+1]);
  1330.             K := N;
  1331.             for g in normals[index] do
  1332.                 K := Closure(K,g);
  1333.             od;
  1334.  
  1335.             # N has trivial radical; check whether K has nontrivial one
  1336.             # K=N is possible when we work in homomorphic images of original
  1337.             if Size(K)=Size(N)  then
  1338.                 index := index-1;
  1339.             else
  1340.                 C := CentralizerNormalCSPG(K,N);
  1341.  
  1342.                 # radical of K is cyclic or trivial; it has to show up in C
  1343.                 if IsTrivial(C)  then
  1344.                     index := index-1;
  1345.                 else
  1346.                     H := NormalClosure(G,C);
  1347.                     series := DerivedSeries(H);
  1348.                     H := series[Length(series)-1];
  1349.  
  1350.                     # at that moment, H is abelian normal in G
  1351.                     # define new action of G with H in the kernel
  1352.                     actionlist := ActionAbelianCSPG(H,n);
  1353.  
  1354.                     # find new action of subgroups in composition series
  1355.                     for i in [1..index] do
  1356.                         for j in [1..Length(normals[i])] do
  1357.                             normals[i][j] :=
  1358.                                 ImageOnAbelianCSPG(normals[i][j],actionlist);
  1359.                         od;
  1360.                     od;
  1361.                     image := [];
  1362.                     for i in [1..Length(G.generators)] do
  1363.                         image[i] :=
  1364.                               ImageOnAbelianCSPG(G.generators[i],actionlist);
  1365.                     od;
  1366.  
  1367.                     # take homomorphic image of G
  1368.                     GG := Group(image,());
  1369.                     hom := GroupHomomorphismByImages(G,GG,
  1370.                                                      G.generators,image);
  1371.                     Add(homlist,hom);
  1372.                     G := GG;
  1373.                     index := index-1;
  1374.  
  1375.                 fi;         # IsTrivial(C)
  1376.  
  1377.             fi;             # Size(K)=Size(N)
  1378.  
  1379.         fi;                 # Length(primes)>1
  1380.  
  1381.     od;
  1382.  
  1383.     # create output;
  1384.     # the radical is the kernel of homomorphisms applied to workgroup
  1385.     lenhomlist := Length(homlist);
  1386.     if lenhomlist = 0  then
  1387.         solvable := [()];
  1388.     else
  1389.         solvable := [];
  1390.         for i in [1..lenhomlist] do
  1391.             for g in Kernel(homlist[i]).generators do
  1392.                 for j in [1..i-1] do
  1393.                     g := PreImagesRepresentative(homlist[i-j],g);
  1394.                 od;
  1395.                 Add(solvable,g);
  1396.             od;
  1397.         od;
  1398.     fi;
  1399.  
  1400.     return Subgroup(Parent(workgroup),solvable);
  1401. end;
  1402.  
  1403.  
  1404. #############################################################################
  1405. ##
  1406. #F  Centre()  . . . . . . . . . . . . . . . . . center of a permutation group
  1407. ##
  1408. ##  constructs the center of G.
  1409. ##  Reference: Beals-Seress, 24th Symp. on Theory of Computing 1992, sect. 9
  1410. ##
  1411. PermGroupOps.Centre := function(G)
  1412.     local   n,          # degree of G
  1413.             orbits,     # list of orbits of G
  1414.             list,       # ordering of permutation domain
  1415.                         # such that G orbits are consecutive
  1416.             base,       # lexicographically smallest (in list) base of G
  1417.             i,j,        # loop variables
  1418.             reps,       # array recording which orbit of G the points in
  1419.                         # perm. domain belong to
  1420.             domain,     # union of G orbits which contain base points
  1421.             significant,# indices of orbits in "orbits" that belong to domain
  1422.             max,        # loop variable, used at definition of significant
  1423.             len,        # length of domain
  1424.             tchom,      # trans. const. homom, restricting G to domain
  1425.             GG,         # the image of tchom
  1426.             orbit,      # an orbit of GG
  1427.             tchom2,     # trans. const. homom, restricting GG to orbit
  1428.             GGG,        # the image of GG at tchom2
  1429.             hgens,      # list of generators for the direct product of
  1430.                         # centralizers of GG in Sym(orbit), for orbits of GG
  1431.             centr,      # the centralizer of GG in Sym(orbit)
  1432.             inverse2,   # inverse of the conjugating permutation of tchom2
  1433.             g,          # generator of centr
  1434.             image,      # generator of centr, as it acts on domain
  1435.             cent;       # center of GG
  1436.  
  1437.     if IsTrivial(G)  then
  1438.         return Group(());
  1439.     fi;
  1440.  
  1441.     n := PermGroupOps.LargestMovedPoint(G);
  1442.     orbits := Orbits(G,[1..n]);
  1443.     orbits := List( orbits, Set );
  1444.  
  1445.     # handle case of transitive G directly
  1446.     if Length(orbits) = 1  then
  1447.         centr := CentralizerTransSymmCSPG(G);
  1448.         cent := IntersectionNormalClosurePermGroup(G,centr);
  1449.         return AsSubgroup(Parent(G),cent);
  1450.     fi;
  1451.  
  1452.     # for intransitive G, create stabilizer chain which fixes an orbit
  1453.     # completely before it takes base points from other orbits
  1454.     reps := [];
  1455.     for i in [1..Length(orbits)] do
  1456.         for j in [1..Length(orbits[i])] do
  1457.             reps[orbits[i][j]] := i;
  1458.         od;
  1459.     od;
  1460.     list := Concatenation(orbits);
  1461.     MakeStabChain(G,list);
  1462.  
  1463.     # take union of significant orbits which contain base points
  1464.     base := Base(G);
  1465.     max := reps[base[1]];
  1466.     significant := [max];
  1467.     domain := Copy(orbits[max]);
  1468.     for i in [1..Length(base)] do
  1469.         if reps[base[i]] > max  then
  1470.             max := reps[base[i]];
  1471.             Append(domain,orbits[max]);
  1472.             Add(significant,max);
  1473.         fi;
  1474.     od;
  1475.     len := Length(domain);
  1476.  
  1477.     # restrict G to significant orbits
  1478.     tchom := OperationHomomorphism(G,Operation(G,domain));
  1479.     GG := Image(tchom,G);
  1480.  
  1481.     # handle case of transitive GG directly
  1482.     if Length(significant) = 1  then
  1483.         centr := CentralizerTransSymmCSPG(GG);
  1484.         cent := IntersectionNormalClosurePermGroup(GG,centr);
  1485.         return PreImages(tchom,cent);
  1486.     fi;
  1487.  
  1488.     # case of intransitive GG
  1489.     # for each orbit of GG, construct generators of centralizer of GG in
  1490.     # Sym(orbit).  hgens is a list of generators for the direct product of
  1491.     # these centralizers.
  1492.     # the group generated by hgens contains the center of GG
  1493.     hgens := [];
  1494.     for i in significant do
  1495.         orbit := OnSets(orbits[i],tchom.conperm);
  1496.         tchom2 := OperationHomomorphism(GG,Operation(GG,orbit));
  1497.         GGG := Image(tchom2,GG);
  1498.         centr := CentralizerTransSymmCSPG(GGG);
  1499.         inverse2 := tchom2.conperm^(-1);
  1500.         for g in centr.generators do
  1501.             g := ListPerm(g);
  1502.  
  1503.             # construct how g acts on domain
  1504.             image := [1..len];
  1505.             for j in [1..Length(orbit)] do
  1506.                 if IsBound(g[j])  then
  1507.                     image[j^inverse2] := g[j]^inverse2;
  1508.                 else
  1509.                     image[j^inverse2] := j^inverse2;
  1510.                 fi;
  1511.             od;
  1512.             Add(hgens,PermList(image));
  1513.         od;
  1514.     od;
  1515.  
  1516.     cent := IntersectionNormalClosurePermGroup(GG,Group(hgens,()));
  1517.     return PreImages(tchom,cent);
  1518. end;
  1519.  
  1520.  
  1521. #############################################################################
  1522. ##
  1523. #F  CentralizerNormalCSPG() . . . . . . . .  centralizer of a normal subgroup
  1524. ##
  1525. ##  computes the centralizer of a NORMAL subgroup N in G.
  1526. ##  Reference: Luks-Seress
  1527. ##
  1528. CentralizerNormalCSPG := function(G,N)
  1529.     local   n,          # degree of G
  1530.             orbits,     # list of orbits of G
  1531.             list,       # ordering of permutation domain
  1532.                         # such that G orbits are consecutive
  1533.             base,       # lexicographically smallest (in list) base of G
  1534.             i,j,        # loop variables
  1535.             reps,       # array recording which orbit of G the points in
  1536.                         # perm. domain belong to
  1537.             domain,     # union of G orbits which contain base points
  1538.             significant,# indices of orbits in "orbits" that belong to domain
  1539.             max,        # loop variable, used at definition of significant
  1540.             len,        # length of domain
  1541.             tchom,      # trans. const. homom, restricting G to domain
  1542.             GG,         # the image of G at tchom
  1543.             NNgens,     # the image of N.generators at tchom
  1544.             orbit,      # an orbit of GG
  1545.             tchom2,     # trans. const. homom, restricting GG to orbit
  1546.             GGG,        # the image of GG at tchom2
  1547.             NNNgens,    # the image of NN.generators at tchom2
  1548.             hgens,      # list of generators for the direct product of
  1549.                         # centralizers of NN in GG restricted to Sym(orbit),
  1550.                         # for orbits of GG
  1551.             centrnorm,  # centralizer of NN in GG restricted to Sym(orbit)
  1552.             inverse2,   # inverse of the conjugating permutation of tchom2
  1553.             g,          # loop variable for generators
  1554.             image,      # generator of centrnorm, as it acts on domain
  1555.             central;    # centralizer of NN in GG
  1556.  
  1557.     if IsTrivial(N)  then
  1558.         return G;
  1559.     fi;
  1560.  
  1561.     n := PermGroupOps.LargestMovedPoint(G);
  1562.     orbits := Orbits(G,[1..n]);
  1563.     orbits := List( orbits, Set );
  1564.  
  1565.     # handle case of transitive G directly
  1566.     if Length(orbits) = 1  then
  1567.         centrnorm := CentralizerNormalTransCSPG(G,N);
  1568.         return centrnorm;
  1569.     fi;
  1570.  
  1571.     # for intransitive G, create stabilizer chain which fixes an orbit
  1572.     # completely before it takes base points from other orbits
  1573.     reps := [];
  1574.     for i in [1..Length(orbits)] do
  1575.         for j in [1..Length(orbits[i])] do
  1576.             reps[orbits[i][j]] := i;
  1577.         od;
  1578.     od;
  1579.     list := Concatenation(orbits);
  1580.     MakeStabChain(G,list);
  1581.  
  1582.     # take union of significant orbits which contain base points
  1583.     base := Base(G);
  1584.     max := reps[base[1]];
  1585.     significant := [max];
  1586.     domain := Copy(orbits[max]);
  1587.     for i in [1..Length(base)] do
  1588.         if reps[base[i]] > max  then
  1589.             max := reps[base[i]];
  1590.             Append(domain,orbits[max]);
  1591.             Add(significant,max);
  1592.         fi;
  1593.     od;
  1594.     len := Length(domain);
  1595.  
  1596.     # restrict G to significant orbits
  1597.     tchom := OperationHomomorphism(G,Operation(G,domain));
  1598.     GG := Image(tchom,G);
  1599.  
  1600.     # restrict N to significant orbits
  1601.     NNgens := [];
  1602.     for g in N.generators do
  1603.         Add(NNgens,Image(tchom,g));
  1604.     od;
  1605.  
  1606.     # handle case of transitive GG directly
  1607.     if Length(significant) = 1  then
  1608.         centrnorm := CentralizerNormalTransCSPG(GG,Group(NNgens,()));
  1609.         return PreImages(tchom,centrnorm);
  1610.     fi;
  1611.  
  1612.     # case of intransitive GG
  1613.     # for each GG orbit, compute the centralizer of NN in GG, restricted to
  1614.     # the orbit. hgens contains generators for the direct product of these
  1615.     # centralizers; the group generated by hgens contains the centralizer of
  1616.     # NN in GG
  1617.     hgens := [];
  1618.     for i in significant do
  1619.         orbit := OnSets(orbits[i],tchom.conperm);
  1620.  
  1621.         # restrict GG, NN to orbit
  1622.         tchom2 := OperationHomomorphism(GG,Operation(GG,orbit));
  1623.         GGG := Image(tchom2,GG);
  1624.         NNNgens := [];
  1625.         for g in NNgens do
  1626.             Add(NNNgens,Image(tchom2,g));
  1627.         od;
  1628.  
  1629.         # compute centralizer of NNN in GGG
  1630.         centrnorm := CentralizerNormalTransCSPG(GGG,Group(NNNgens,()));
  1631.         inverse2 := tchom2.conperm^(-1);
  1632.  
  1633.         # determine how the centralizer acts on domain
  1634.         for g in centrnorm.generators do
  1635.             g := ListPerm(g);
  1636.             image := [1..len];
  1637.             for j in [1..Length(orbit)] do
  1638.                 if IsBound(g[j])  then
  1639.                     image[j^inverse2] := g[j]^inverse2;
  1640.                 else
  1641.                     image[j^inverse2] := j^inverse2;
  1642.                 fi;
  1643.             od;
  1644.             Add(hgens,PermList(image));
  1645.         od;
  1646.     od;
  1647.  
  1648.     central := IntersectionNormalClosurePermGroup(GG,Group(hgens,()));
  1649.     return PreImages(tchom,central);
  1650. end;
  1651.  
  1652.  
  1653. #############################################################################
  1654. ##
  1655. #F  CentralizerNormalTransCSPG()  . . . centralizer of normal in transitive G
  1656. ##
  1657. ##  computes C_G(N) with G transitive, N normal in G
  1658. ##  reference: Luks-Seress
  1659. ##
  1660. CentralizerNormalTransCSPG := function(G,N)
  1661.     local   n,          # degree of G
  1662.             x,          # the first base point of G
  1663.             stabgroup,  # stabilizer of x in N
  1664.             U,          # an orbit of centralizer of N in S_n
  1665.             orbits,     # list of orbits of centralizer of N is S_n
  1666.             bhom,       # block homomorphism from G to action on orbits
  1667.             GG,         # the kernel of bhom
  1668.             Ginverses,  # list of inverses of generators of G
  1669.             Ninverses,  # list of inverses of generators of N
  1670.             norbits,    # list of orbits of N
  1671.             orbitlength,# the length of the N orbits
  1672.                         # (all are of the same size)
  1673.             reprlist,   # list recording which orbit of N contains a point of
  1674.                         # permutation domain
  1675.             positionlist,
  1676.                         # list recording the position of a point within its
  1677.                         # N orbit
  1678.             positiongenlist,
  1679.                         # list of length orbitlength; i^th entry records
  1680.                         # the position of the generator in N.generators
  1681.                         # which occurs in N.transversal at N.orbit[i]
  1682.             len,        # number of N orbits intersecting U
  1683.             diff,       # loop variable denoting a subset of U
  1684.                         # used at creation of N orbits which intersect U
  1685.             new,        # an orbit of N intersecting U
  1686.             i,j,k,m,    # loop variables
  1687.             y,u,s,      # points of permutation domain
  1688.             set,        # loop variable denoting subset of [1..n], used at
  1689.                         # creation of covering of [1..n] by orbits of N
  1690.             newlen,     # loop variable counting the total length of N orbits
  1691.                         # at the covering of [1..n]
  1692.             word,       # a coset representative of G or N, as a word
  1693.             tchom,      # transitive constituent homomorphism restricting
  1694.                         # N to N.orbit
  1695.             inverse,    # the inverse of tchom.conperm
  1696.             centr,      # the centralizer of N in Sym(N.orbit)
  1697.             hom,        # homomorphism of GG whose kernel is C_G(N)
  1698.             images,     # list of images of generators of GG at hom
  1699.             top,bottom,g,
  1700.                         # permutations used at the creation of images
  1701.             K;          # image of GG at hom
  1702.  
  1703.     if IsTrivial(N)  then
  1704.         return G;
  1705.     fi;
  1706.  
  1707.     n := PermGroupOps.LargestMovedPoint(G);
  1708.     MakeStabChain(G);
  1709.     x := G.orbit[1];
  1710.     MakeStabChain(N,[x]);
  1711.     stabgroup := Stabilizer(N,x,OnPoints);
  1712.     U := Difference([1..n],PermGroupOps.MovedPoints(stabgroup));
  1713.     orbits := Orbit(G,U,OnSets);
  1714.  
  1715.     # orbits contains the orbits of the centralizer of N in S_n;
  1716.     # so C_G(N) must fix setwise the elements of orbits
  1717.     bhom := OperationHomomorphism(G,Operation(G,orbits,OnSets));
  1718.     GG := Kernel(bhom);
  1719.     if IsTrivial(GG)  then
  1720.         return Subgroup(Parent(G),[()]);
  1721.     fi;
  1722.  
  1723.     Ginverses := [];
  1724.     for i in [1..Length(G.generators)] do
  1725.         Ginverses[i] := G.generators[i]^(-1);
  1726.     od;
  1727.  
  1728.     Ninverses := [];
  1729.     for i in [1..Length(N.generators)] do
  1730.         Ninverses[i] := N.generators[i]^(-1);
  1731.     od;
  1732.  
  1733.     # we partition [1..n] into the orbits of N, and compute the
  1734.     # identification between equivalent orbits (equivalent in the sense
  1735.     # that the centralizer of N in S_n exchanges them). After that, we
  1736.     # conjugate the union of equivalent orbits to cover [1..n]
  1737.     norbits := [N.orbit];
  1738.     orbitlength := Length(N.orbit);
  1739.     positionlist := [];
  1740.     reprlist := [];
  1741.     positiongenlist := [];
  1742.     for i in [1..orbitlength] do
  1743.         positionlist[N.orbit[i]] := i;
  1744.         reprlist[N.orbit[i]] := 1;
  1745.         positiongenlist[i]:=Position(N.generators,N.transversal[N.orbit[i]]);
  1746.     od;
  1747.     diff := Difference(U,norbits[1]);
  1748.     len := 1;
  1749.  
  1750.     # create the orbits of N equivalent to the first one
  1751.     while diff <> [] do
  1752.         len := len+1;
  1753.         y := diff[1];
  1754.         new := [y];
  1755.         positionlist[y] := 1;
  1756.         reprlist[y] := len;
  1757.         for i in [2..orbitlength] do
  1758.             u := N.orbit[i]^N.generators[positiongenlist[i]];
  1759.             new[i] := new[positionlist[u]]^Ninverses[positiongenlist[i]];
  1760.             positionlist[new[i]] := i;
  1761.             reprlist[new[i]] := len;
  1762.         od;
  1763.         Add(norbits,new);
  1764.         diff := Difference(diff,new);
  1765.     od;
  1766.  
  1767.     # if the domain is not covered, create further orbits of N
  1768.     if len*orbitlength < n  then
  1769.         set := Difference([1..n],Union(norbits));
  1770.         for k in [2..n/(len*orbitlength)] do
  1771.             newlen := (k-1)*len;
  1772.             y := set[1];
  1773.             word := CosetRepAsWord(x,y,G.transversal);
  1774.             word := InverseAsWord(word,G.generators,Ginverses);
  1775.             for i in [1..len] do
  1776.                 norbits[newlen+i] := [];
  1777.                 for j in [1..orbitlength] do
  1778.                     norbits[newlen+i][j] := ImageInWord(norbits[i][j],word);
  1779.                     positionlist[norbits[newlen+i][j]] := j;
  1780.                     reprlist[norbits[newlen+i][j]] := newlen+i;
  1781.                 od;
  1782.                 set := Difference(set,norbits[newlen+i]);
  1783.             od;
  1784.         od;
  1785.     fi;
  1786.  
  1787.     # compute centralizer of N in first orbit; centralizer in other orbits
  1788.     # is obtained from identification between orbits
  1789.     tchom := OperationHomomorphism(N,Operation(N,N.orbit));
  1790.     inverse := tchom.conperm^(-1);
  1791.     centr := CentralizerTransSymmCSPG(Image(tchom,N));
  1792.  
  1793.     # compute transversal of centr
  1794.     centr.orbit := [x^tchom.conperm];
  1795.     centr.transversal := [];
  1796.     centr.transversal[x^tchom.conperm] := ();
  1797.     PermGroupOps.AddGensExtOrb(centr,centr.generators);
  1798.  
  1799.     # compute images at homomorphism of GG, g -> g c_g^{-1} (cf. Luks-Seress)
  1800.     # the kernel of this homomorphism is C_G(N)
  1801.     images := [];
  1802.     for i in [1..Length(GG.generators)] do
  1803.         images[i] := [];
  1804.  
  1805.         # top is the permutation in the wreath product which pulls back g to
  1806.         # orbits of N
  1807.         top := [];
  1808.         for j in [1..Length(norbits)] do
  1809.             k := reprlist[norbits[j][1]^GG.generators[i]];
  1810.             for m in [1..orbitlength] do
  1811.                 top[norbits[k][m]] := norbits[j][m];
  1812.             od;
  1813.         od;
  1814.         top := PermList(top);
  1815.         g := GG.generators[i]*top;
  1816.  
  1817.         # pull back each leading point in norbits by centralizer of N
  1818.         bottom := [];
  1819.         for j in [1..Length(norbits)] do
  1820.             k := positionlist[norbits[j][1]^g];
  1821.             word := CosetRepAsWord(x^tchom.conperm,N.orbit[k]^tchom.conperm,
  1822.                                  centr.transversal);
  1823.             for m in [1..orbitlength] do
  1824.                 s := (ImageInWord(N.orbit[m]^tchom.conperm,word))^inverse;
  1825.                 bottom[norbits[j][m]] := norbits[j][positionlist[s]];
  1826.             od;
  1827.          od;
  1828.          bottom := PermList(bottom);
  1829.          images[i] := g*bottom;
  1830.     od;
  1831.  
  1832.     K := Group(images,());
  1833.     hom := GroupHomomorphismByImages(GG,K,GG.generators,images);
  1834.     return Kernel(hom);
  1835. end;
  1836.  
  1837.  
  1838. #############################################################################
  1839. ##
  1840. #F  CentralizerTransSymmCSPG()  . . . . .  centralizer of transitive G in S_n
  1841. ##
  1842. ##  computes the centralizer of a transitive group G in S_n
  1843. ##
  1844. CentralizerTransSymmCSPG := function(G)
  1845.     local   n,          # the degree of G
  1846.             stabgroup,  # subgroup of G stabilizing first base point
  1847.             x,          # the first base point
  1848.             L,          # the set of fixed points of stabgroup
  1849.             orbitx,     # the orbit of x in the centralizer;
  1850.                         # eventually, orbitx=L
  1851.             y,          # a point in L
  1852.             z,          # loop variable running through permutation domain
  1853.             i,          # loop variable
  1854.             h,          # a coset representative of G, written as word in the
  1855.                         # generators
  1856.             gens,       # list of generators for the centralizer
  1857.             gen,        # an element of gens
  1858.             Ginverses;  # list of inverses for the generators of G
  1859.  
  1860.     if IsTrivial(G)  then
  1861.         return Subgroup(Parent(G),[()]);
  1862.     fi;
  1863.  
  1864.     n := PermGroupOps.LargestMovedPoint(G);
  1865.     MakeStabChain(G);
  1866.     x := G.orbit[1];
  1867.     stabgroup := Stabilizer(G,x,OnPoints);
  1868.     L := Difference([1..n],PermGroupOps.MovedPoints(stabgroup));
  1869.  
  1870.     Ginverses := [];
  1871.     for i in [1..Length(G.generators)] do
  1872.         Ginverses[i] := G.generators[i]^(-1);
  1873.     od;
  1874.     Add(G.generators,());
  1875.     Add(Ginverses,());
  1876.  
  1877.     # the centralizer of G is semiregular, acting transitively on L
  1878.     orbitx := [];
  1879.     gens := [];
  1880.     while Length(orbitx) < Length(L) do
  1881.  
  1882.         # construct element of centralizer which carries x to new point in L
  1883.         gen := [];
  1884.         y := Difference(L,orbitx)[1];
  1885.         for z in [1..n] do
  1886.             h := CosetRepAsWord(x,z,G.transversal);
  1887.             h := InverseAsWord(h,G.generators,Ginverses);
  1888.             gen[z] := ImageInWord(y,h);
  1889.         od;
  1890.         Add(gens,PermList(gen));
  1891.         orbitx := Orbit(Group(gens,()),x,OnPoints);
  1892.     od;
  1893.  
  1894.     Unbind(G.generators[Length(G.generators)]);
  1895.     return Group(gens,());
  1896. end;
  1897.  
  1898.  
  1899. #############################################################################
  1900. ##
  1901. #F  IntersectionNormalClosurePermGroup(<G>,<H>) . . . . . . . intersection of
  1902. #F                                   normal closure of <H> under <G> with <G>
  1903. ##
  1904. ##  computes $H^G \cap G$
  1905. ##
  1906. IntersectionNormalClosurePermGroup := function(G,H)
  1907.     local   n,          # maximum of degrees of G,H
  1908.             i,j,        # loop variables
  1909.             extended,   # action of generators of G,H on 2n points
  1910.             newgens,    # set of extended generators
  1911.             group;      # the group generated by newgens
  1912.                         # stabilizing the second n points, we get H^G \cap G
  1913.  
  1914.     if IsTrivial(G) or IsTrivial(H)  then
  1915.         return Group(());
  1916.     fi;
  1917.  
  1918.     n := Maximum(PermGroupOps.LargestMovedPoint(G),
  1919.                  PermGroupOps.LargestMovedPoint(H));
  1920.     newgens := [];
  1921.  
  1922.     # extend the generators of G acting on [n+1..2n] exactly as on [1..n]
  1923.     for i in [1..Length(G.generators)] do
  1924.         extended := ListPerm(G.generators[i]);
  1925.         for j in [1..n] do
  1926.             if not IsBound(extended[j])  then
  1927.                 extended[j] := j;
  1928.             fi;
  1929.             extended[n+j] := n+extended[j];
  1930.         od;
  1931.         Add(newgens,PermList(extended));
  1932.     od;
  1933.  
  1934.     # from the generators of H, create permutations which act on [n+1..2n]
  1935.     # as the original generator on [1..n] and which act trivially on [1..n]
  1936.     for i in [1..Length(H.generators)] do
  1937.         extended := ListPerm(H.generators[i]);
  1938.         for j in [1..n] do
  1939.             if not IsBound(extended[j])  then
  1940.                 extended[j] := j;
  1941.             fi;
  1942.             extended[n+j] := n+extended[j];
  1943.             extended[j] := j;
  1944.         od;
  1945.         Add(newgens,PermList(extended));
  1946.     od;
  1947.  
  1948.     group := Group(newgens,());
  1949.     MakeStabChain(group,[n+1..2*n]);
  1950.     # for i in [n+1..2*n] do
  1951.     #     group := Stabilizer(group,i,OnPoints);
  1952.     # od;
  1953.     group := Stabilizer(group,[n+1 .. 2*n],OnTuples);
  1954.     return group;
  1955. end;
  1956.  
  1957.  
  1958. #############################################################################
  1959. ##
  1960. #F  ActionAbelianCSPG() . . . . . . . . . action of abelian permutation group
  1961. ##
  1962. ##  given an abelian subgroup H of S_n, the routine codes the action of
  1963. ##  H on its orbits. The output is an array of length 7, describing this
  1964. ##  action; the components of this array are described at the local variable
  1965. ##  section
  1966. ActionAbelianCSPG := function(H,n)
  1967.     local   i,j,k,      # loop variables
  1968.             orbits,     # list of orbits of H; 6th element of output
  1969.             action,     # list; the i^th element contains a list of
  1970.                         # generators for the action of H on i^th orbit
  1971.             inverse,    # inverse[i][k] is the inverse of action[i][k]
  1972.                         # 1st element of output
  1973.             C,          # C[i] is the group generated by action[i]
  1974.                         # 2nd element of output
  1975.             positionlist,
  1976.                         # for i in [1..n], positionlist[i] gives the position
  1977.                         # of i in its H orbit. 3rd element of output
  1978.             reprlist,   # for i in [1..n], reprlist[i] gives the position of
  1979.                         # the H orbit of i in orbits. 4th element of output
  1980.             cpositiongenlist,
  1981.                         # cpositionlength[i][k] gives the position in
  1982.                         # action[i] of the C[i] generator which occurs in
  1983.                         # C[i].transversal[k]. 5th element of output
  1984.             cumulativelength;
  1985.                         # cumulativelength[i] is the sum of lengths of first
  1986.                         # i-1 elements of orbits. 7th element of output
  1987.  
  1988.     orbits := Orbits(H,[1..n]);
  1989.     cumulativelength := [0];
  1990.     for i in [1..Length(orbits)-1] do
  1991.         cumulativelength[i+1] := cumulativelength[i]+Length(orbits[i]);
  1992.     od;
  1993.  
  1994.     positionlist := [];
  1995.     reprlist := [];
  1996.     for i in [1..Length(orbits)] do
  1997.         for j in [1..Length(orbits[i])] do
  1998.             positionlist[orbits[i][j]] := j;
  1999.             reprlist[orbits[i][j]] := i;
  2000.         od;
  2001.     od;
  2002.  
  2003.     # action[i][k] is the action of H.generators[k] on the i^th orbit of H,
  2004.     # viewed as a permutation on [1..Length(orbits[i])]
  2005.     action := [];
  2006.     inverse := [];
  2007.     for i in [1..Length(orbits)] do
  2008.         action[i] := [];
  2009.         inverse[i] := [];
  2010.         for k in [1..Length(H.generators)] do
  2011.             action[i][k] := [];
  2012.             for j in [1..Length(orbits[i])] do
  2013.                 action[i][k][j]:=positionlist[orbits[i][j]^H.generators[k]];
  2014.             od;
  2015.             action[i][k] := PermList(action[i][k]);
  2016.             inverse[i][k] := action[i][k]^(-1);
  2017.         od;
  2018.     od;
  2019.  
  2020.     C := [];
  2021.     cpositiongenlist := [];
  2022.     for i in [1..Length(orbits)] do
  2023.         cpositiongenlist[i] := [];
  2024.         C[i] := Group(action[i],());
  2025.  
  2026.         # create transversal of C[i]
  2027.         C[i].orbit := [1];
  2028.         C[i].transversal := [()];
  2029.         Add(action[i],());
  2030.         Add(inverse[i],());
  2031.         PermGroupOps.AddGensExtOrb(C[i],C[i].generators);
  2032.  
  2033.         # determine position of generators occuring in transversal
  2034.         for j in [1..Length(C[i].orbit)] do
  2035.             cpositiongenlist[i][j]:=Position(action[i],C[i].transversal[j]);
  2036.         od;
  2037.     od;
  2038.  
  2039.     return [inverse,C,positionlist,reprlist,
  2040.             cpositiongenlist,orbits,cumulativelength];
  2041. end;
  2042.  
  2043.  
  2044. #############################################################################
  2045. ##
  2046. #F  ImageOnAbelianCSPG()  . image of normalizing element on orbits of abelian
  2047. ##
  2048. ##  given the action of an abelian group H encoded in actionlist by the
  2049. ##  subroutine ActionAbelianCSPG, and a permutation g normalizing H,
  2050. ##  this subroutine computes the conjugation action of g on the transitive
  2051. ##  constituent pieces of H
  2052. ##
  2053. ImageOnAbelianCSPG := function(g,actionlist)
  2054.     local   i,s,        # loop variables
  2055.             orbits,     # list of orbits of H
  2056.       # let action denote the list with the i^th element containing a list of
  2057.       # generators for the action of H on i^th orbit
  2058.             inverse,    # inverse[i][k] is the inverse of action[i][k]
  2059.             C,          # C[i] is the group generated by action[i]
  2060.             positionlist,
  2061.                         # for i in [1..n], positionlist[i] gives the position
  2062.                         # of i in its H orbit
  2063.             reprlist,   # for i in [1..n], reprlist[i] gives the position of
  2064.                         # the H orbit of i in orbits
  2065.             cpositiongenlist,
  2066.                         # cpositionlength[i][k] gives the position in
  2067.                         # action[i] of the C[i] generator which occurs in
  2068.                         # C[i].transversal[k]
  2069.             cumulativelength,
  2070.                         # cumulativelength[i] is the sum of lengths of first
  2071.                         # i-1 elements of orbits
  2072.             j,          # index of H-orbit in orbits which is the image of
  2073.                         # the i^th H-orbit
  2074.             x,          # position of element of i^th orbit which is mapped
  2075.                         # by g to first element of j^th orbit
  2076.             inv,        # the inverse of g
  2077.             gimage,     # output of the routine; conjugation action of g
  2078.             image,t;    # see explanation in body of routine
  2079.  
  2080.     inv := g^(-1);
  2081.     gimage := [];
  2082.     inverse := actionlist[1];
  2083.     C := actionlist[2];
  2084.     positionlist := actionlist[3];
  2085.     reprlist := actionlist[4];
  2086.     cpositiongenlist := actionlist[5];
  2087.     orbits := actionlist[6];
  2088.     cumulativelength := actionlist[7];
  2089.  
  2090.     # the transitive constituent pieces of H are regarded as a list of
  2091.     # length n; the (unique) piece carrying the first point of i^th orbit
  2092.     # to k^th point of i^th orbit is in the position cumulativelength[i]+k.
  2093.     # gimage will contain the conjugation action of g on the elements of
  2094.     # this list
  2095.     for i in [1..Length(orbits)] do
  2096.  
  2097.         # determine which orbit contains the images of pieces from i^th orbit
  2098.         j := reprlist[orbits[i][1]^g];
  2099.  
  2100.         # for each piece h from i^th orbit, we have to determine the image of
  2101.         # orbits[j][1] at the permutation g^(-1)*h*g
  2102.         # from regularity of action on orbits, this image determines the
  2103.         # conjugate first, compute the images of orbits[j][1] in g^(-1)*h,
  2104.         # and store the result in the array image. Then determine the
  2105.         # g-image of the result and store it in gimage.
  2106.         # This way, elements in "image" can be used more times,
  2107.         # and the running time is linear (no hidden log factors).
  2108.         x := positionlist[orbits[j][1]^inv];
  2109.         image := [x];
  2110.         gimage[cumulativelength[i]+1] := cumulativelength[j]+1;
  2111.         for s in [2..Length(C[i].orbit)] do
  2112.  
  2113.             # t is the predecessor in Schreier tree of C[i].orbit[s]
  2114.             t := C[i].orbit[s]^C[i].transversal[C[i].orbit[s]];
  2115.             image[C[i].orbit[s]] :=
  2116.                      image[t]^inverse[i][cpositiongenlist[i][C[i].orbit[s]]];
  2117.             gimage[cumulativelength[i]+C[i].orbit[s]] :=
  2118.                      cumulativelength[j]
  2119.                     +positionlist[orbits[i][image[C[i].orbit[s]]]^g];
  2120.         od;
  2121.     od;
  2122.  
  2123.     gimage := PermList(gimage);
  2124.     return gimage;
  2125. end;
  2126.  
  2127.  
  2128. #############################################################################
  2129. ##
  2130. #E  Emacs . . . . . . . . . . . . . . . . . . . . . . . local emacs variables
  2131. ##
  2132. ##  Local Variables:
  2133. ##  mode:               outline
  2134. ##  outline-regexp:     "#A\\|#F\\|#V\\|#E\\|#R"
  2135. ##  tab-width:          2
  2136. ##  fill-column:        73
  2137. ##  fill-prefix:        "##  "
  2138. ##  eval:               (hide-body)
  2139. ##  End:
  2140. ##
  2141.  
  2142.  
  2143.  
  2144.