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

  1. #############################################################################
  2. ##
  3. #A  permag.g                    GAP library                    Heiko Thei"sen
  4. ##
  5. #A  @(#)$Id: permag.g,v 3.7 1993/02/10 10:11:14 martin Rel $
  6. ##
  7. #Y  Copyright 1990-1993,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
  8. ##
  9. ##  This  file  contains  functions  that  calculate  composition  series for
  10. ##  solvable permutation groups and convert such groups into ag groups.
  11. ##
  12. #H  $Log: permag.g,v $
  13. #H  Revision 3.7  1993/02/10  10:11:14  martin
  14. #H  moved 'PermGroupOps.CompositionSeries' to "permcser"
  15. #H
  16. #H  Revision 3.6  1993/02/09  14:25:55  martin
  17. #H  made undefined globals local
  18. #H
  19. #H  Revision 3.5  1993/01/25  13:02:33  fceller
  20. #H  changed names of abstract gens to "g"
  21. #H
  22. #H  Revision 3.4  1993/01/15  14:46:54  theissen
  23. #H  fixed a minor bug to allow ag conversion of the trivial group
  24. #H
  25. #H  Revision 3.3  1993/01/12  11:49:06  theissen
  26. #H  implemented the use of a bssgs to save memory
  27. #H
  28. #H  Revision 3.2  1992/12/02  09:01:10  fceller
  29. #H  Initial GAP 3.2 revision
  30. #H
  31. ##
  32.  
  33.  
  34. #############################################################################
  35. ##
  36. #F  MaximalBlocksPGroup( <G>, <D> ) . . . . . . . find a maximal block system
  37. ##
  38. MaximalBlocksPGroup := function( G, D )
  39.     local   B;
  40.  
  41.     D := Blocks( G, D );
  42.     if Length(D) = 1  then
  43.         return D;
  44.     fi;
  45.     B := D;
  46.     while not IsPrime(Length(D))  do
  47.         G := Operation( G, D, OnSets );
  48.         D := Blocks( G, [1..Length(G.operationDomain)] );
  49.         B := List( D, d -> Concatenation( Sublist( B, d ) ) );
  50.     od;
  51.     return B;
  52. end;
  53.  
  54.  
  55. #############################################################################
  56. ##
  57. #F  OrderFactorGroupElement(<G>,<g>)  . . . . . . . . order of G*g in <G,g>/G
  58. ##
  59. OrderFactorGroupElement := function( G, g )
  60.     local   ord, div;
  61.  
  62.     if g = G.identity  then
  63.         return 1;
  64.     fi;
  65.     ord := Order( G, g );
  66.     for div  in Set(FactorsInt(ord))  do
  67.         while ord mod div = 0 and g^(ord/div) in G  do
  68.             ord := ord / div;
  69.         od;
  70.     od;
  71.     return ord;
  72. end;
  73.  
  74.  
  75. #############################################################################
  76. ##
  77. #F  InsertStabChain( <G>, <base>, <sgs> ) . . . . . insert a stabilizer chain
  78. ##
  79. ##  This function inserts a stabilizer chain in the group record of <G> along
  80. ##  the base <base> using the strong generating set <sgs>.
  81. ##
  82. InsertStabChain := function( G, base, sgs )
  83.     local   pt,  orb;
  84.  
  85.     if 0 < Length(base)  then
  86.         pt  := base[1];
  87.         orb := PermGroupOps.OrbitTransversal( G, pt );
  88.         G.orbit       := orb.orbit;
  89.         G.transversal := orb.transversal;
  90.         G.stabilizer  := rec( identity   := G.identity,
  91.                               generators := Filtered( sgs, s->pt^s=pt ) );
  92.         InsertStabChain( G.stabilizer,
  93.                          Sublist( base, [ 2 .. Length(base) ] ),
  94.                          G.stabilizer.generators );
  95.     fi;
  96. end;
  97.  
  98.  
  99. #############################################################################
  100. ##
  101. #F  ClosureNormalizingElementPermGroup( <H>, <y> )  .  closure of <H> and <y>
  102. ##
  103. ##  This function uses an idea  of C. Sims  to extend a permutation group <H>
  104. ##  with a normalizing element <y>, also extending the stabilizer chain.  The
  105. ##  method is faster than the standard algorithm because a generating set for
  106. ##  each  stabilizer in the chain can easily be obtained and the extension of
  107. ##  the basic  orbits does  not require a full orbit  algorithm.  Besides, if
  108. ##  '<H>.bssgs'  is  a base-strong  subnormal generating system upon  call of
  109. ##  this procedure the  result will  also have  bound a system '.bssgs'  with
  110. ##  this property.
  111. ##
  112. ##  <y> must be an element of the parent of <H> and must normalize <H>.  This
  113. ##  is *not* checked.
  114. ##
  115. ClosureNormalizingElementPermGroup := function( H, y )
  116.     local  H,  G,  z,  newgens,  orbit,  pnt,  o,  w,  m,  p;
  117.  
  118.     # Do not change the original argument.
  119.     H := Copy( H );
  120.     G := H;
  121.     z := y;
  122.     if IsBound( H.bssgs )  then
  123.     newgens := [  ];
  124.     fi;
  125.  
  126.     while z <> H.identity  do
  127.  
  128.     # If necessary, extend the base.
  129.     if not IsBound( G.orbit )  then
  130.         G.orbit                     := [ LargestMovedPointPerm( z ) ];
  131.         G.transversal            := [  ];
  132.         G.transversal[ G.orbit[1] ] := H.identity;
  133.         G.stabilizer           := rec( identity   := H.identity,
  134.                         generators := [  ] );
  135.         fi;
  136.  
  137.     # Extend the orbit with the new generator.
  138.     orbit := ShallowCopy( G.orbit );
  139.     pnt := orbit[ 1 ];
  140.     m := 1;
  141.     w := z;
  142.     while not pnt/w in orbit  do
  143.         for o  in orbit  do
  144.         Add( G.orbit, o/w );
  145.         G.transversal[ o/w ] := z;
  146.         od;
  147.         m := m+1;
  148.         w := w*z;
  149.     od;
  150.  
  151.           # Put in <z> as generator in the stabilizer chain.
  152.     if not z in G.generators  then
  153.         Add( G.generators, z );
  154.     fi;
  155.  
  156.     # If a bssgs is  present  and the orbit is  properly extended, put in
  157.     # <z> and its  powers as new polycyclic generators, otherwise let <z>
  158.     # = <w>.
  159.     if m > 1  then
  160.         if IsBound( newgens )  then
  161.         for p  in FactorsInt( m )  do
  162.             Add( newgens, z );
  163.             z := z^p;
  164.         od;
  165.         else
  166.         z := w;
  167.         fi;
  168.     fi;
  169.     
  170.     # Now <z>  = <w>. Find a cofactor to <z> such that  the product fixes
  171.     # <pnt>.
  172.     while pnt^z <> pnt  do
  173.         z := z * G.transversal[ pnt^z ];
  174.     od;
  175.  
  176.     # Go down one step in the stabilizer chain.
  177.     G := G.stabilizer;
  178.  
  179.     od;
  180.  
  181.     # Extend the bssgs of <H> by <newgens> if it is present.
  182.     if IsBound( newgens )  then
  183.     H.bssgs := Concatenation( newgens, H.bssgs );
  184.     fi;
  185.  
  186.     return H;
  187.  
  188. end;
  189.  
  190. #############################################################################
  191. ##
  192.  
  193. #F  PermGroupOps.OrbitTransversal( <G>, <d> ) . . . . . orbit and transversal
  194. ##
  195. PermGroupOps.OrbitTransversal := function( G, d )
  196.     local  max,  orb,  new,  pnt,  gen,  img;
  197.  
  198.     orb := rec( orbit := [d], transversal := [] );
  199.     orb.transversal[d] := G.identity;
  200.     max := 0;
  201.     for gen  in G.generators  do
  202.         if gen <> G.identity and max < LargestMovedPointPerm(gen)  then
  203.             max := LargestMovedPointPerm(gen);
  204.         fi;
  205.     od;
  206.     if d in [1..max]   then
  207.         new := BlistList( [1..max], [1..max] );
  208.         new[d] := false;
  209.         for pnt  in orb.orbit  do
  210.             for gen  in G.generators  do
  211.                 img := pnt/gen;
  212.                 if new[img]  then
  213.                     Add( orb.orbit, img );
  214.                     orb.transversal[img] := gen;
  215.                     new[img] := false;
  216.                 fi;
  217.             od;
  218.         od;
  219.     fi;
  220.     return orb;
  221.  
  222. end;
  223.  
  224.  
  225. #############################################################################
  226. ##
  227. #F  PermGroupOps.TryElementaryAbelianSeries(<G>)  . . . . try to build an eas
  228. ##
  229. ##  This  function starts with  the series (<K>_1) where <K>_1 is the trivial
  230. ##  subgroup     of     <G>     and    extends     this     series    calling
  231. ##  'ExtendElementaryAbelianSeriesPermGroup' with  each generator of  <G>  in
  232. ##  turn. If <G> is solvable this will result in an elementary abelian series
  233. ##  (<G>,...,<K>_1),  otherwise 'ExtendElementaryAbelianSeriesPermGroup' will
  234. ##  return  'false' because <G>.upperBoundDerivedLength, which  is calculated
  235. ##  according to J.D.  Dixon, is exceeded.  If <G> is solvable, this function
  236. ##  binds and  returns  an elementary  abelian  series, otherwise it  returns
  237. ##  'false'.  The flag '<G>.isSolvable' is also set by this function.
  238. ##
  239. PermGroupOps.TryElementaryAbelianSeries := function( G )
  240.     local   N,  y,  base,  n,  c,  log;
  241.  
  242.     # if <G> is trivial return 
  243.     if G.generators = [  ]  then
  244.         G.elementaryAbelianSeries := [ G ];
  245.         G.isSolvable            := true;
  246.         G.bssgs                   := [  ];
  247.         return G.elementaryAbelianSeries;
  248.     else
  249.         n := PermGroupOps.LargestMovedPoint(G);
  250.     fi;
  251.  
  252.     # compute an upper bound for the derived length of <G>, assuming that <G>
  253.     # is solvable. According to Dixon (1968) this is (5 log_3(deg(<G>)))/2
  254.     log := 0;
  255.     c   := 1;
  256.     while c < n  do
  257.         log := log + 1;
  258.         c   := c*3;
  259.     od;
  260.     G.upperBoundDerivedLength := 5*log/2;
  261.  
  262.     # start with the trivial subgroup
  263.     N := TrivialSubgroup(G);
  264.     N.bssgs := [  ];
  265.     InsertStabChain( N, [  ], [  ] );
  266.     G.elementaryAbelianSeries := [ N ];
  267.     for y  in G.generators  do
  268.         N := ExtendElementaryAbelianSeriesPermGroup( G, y, 0 );
  269.         if N = false  then
  270.             G.isSolvable := false;
  271.             Unbind(G.elementaryAbelianSeries);
  272.         Unbind( G.upperBoundDerivedLength );
  273.             return false;
  274.         fi;
  275.     od;
  276.  
  277.     # copy the information stored in <N> to <G>
  278.     G.bssgs       := N.bssgs;
  279.     G.orbit       := N.orbit;
  280.     G.transversal := N.transversal;
  281.     G.stabilizer  := N.stabilizer;
  282.     G.isSolvable  := true;               
  283.     Unbind( G.upperBoundDerivedLength );
  284.  
  285.     return G.elementaryAbelianSeries;
  286.  
  287. end;
  288.  
  289. #############################################################################
  290. ##
  291.  
  292. #F  ExtendElementaryAbelianSeriesPermGroup( <G>, <y>, <der> ) . . . . . local
  293. ##
  294. ##  This function  assumes that <G>  has bound a  partial  elementary abelian
  295. ##  series,  i.e.  a  sequence  (<K>_m,...,<K>_1) of  normal  subgroups  with
  296. ##  elementary  abelian  factors.   It   extends  this  series  to  a  series
  297. ##  (<K>_n,...,<K>_1) where <K>_n is the  normal closure of <K>_m and <y> and
  298. ##  further  normal  subgroups  <K>_{n-1},...,<K>_{m+1} may  be  inserted  to
  299. ##  ensure elementary abelian factors.  The method is due to C. Sims and will
  300. ##  terminate  if <G> is solvable.  To avoid endless loops the function takes
  301. ##  the counter <der>  which must be  0  when this  function is called from a
  302. ##  program and which measures the  depth of  the  commutators we are working
  303. ##  with.  If <G> is solvable, commutators cannot  have arbitrary depth and a
  304. ##  bound on  <der> can  be  calculated  only from  the degree of <G>.   This
  305. ##  function    assumes    that    such    a     bound     is    bound     in
  306. ##  '<G>.upperBoundDerivedLength' and uses  this  trick to detect whether <G>
  307. ##  is not solvable.
  308. ##
  309. ExtendElementaryAbelianSeriesPermGroup := function( G, y, der )
  310.     local   N,  M,  U,  Z,  z,  T,  done,  u,  w,  V,  v,  q;
  311.   
  312.     # if we are too deep in the derived series, then <G> is not solvable 
  313.     if der > G.upperBoundDerivedLength  then
  314.         return false;
  315.     fi;
  316.  
  317.     # try to extend the series
  318.     N := G.elementaryAbelianSeries[1];
  319.     M := N;
  320.     U := [];
  321.     Z := [y];
  322.     for z  in Z  do
  323.         if not z in M  then
  324.             T := U;
  325.             done := false;
  326.             while not done and 0 < Length(T)  do
  327.  
  328.                 # at this point, M=<N,U>
  329.                 u := T[1];
  330.                 T := Sublist( T, [ 2 .. Length(T) ] );
  331.                 w := Comm( u, z );
  332.  
  333.                 # if  <z> and  <u> do  not  commute mod  <N>,  extend <N>  to
  334.                 # NormalClosure( <N>,  [<z>,<u>] ),  going  one step  further
  335.                 # down the derived series
  336.                 if not w in N  then
  337.                     N := ExtendElementaryAbelianSeriesPermGroup(G, w, der+1);
  338.                     if N = false  then
  339.                         return false;
  340.                     fi;
  341.  
  342.                     # set M := <N,U> for the  new N,  V  <  U  contains those
  343.                     # generators that  are actually  needed in the  extension
  344.                     # process, i.e. M=<N,V>
  345.                     M := N;
  346.                     V := [];
  347.                     for v  in U  do
  348.                         if not v in M  then
  349.                             M := ClosureNormalizingElementPermGroup( M, v );
  350.                             AddSet( V, v );
  351.                         else
  352.                             RemoveSet( T, v );
  353.                         fi;
  354.                     od;
  355.  
  356.                     # restore U such that M = <N,U>
  357.                     U := V;
  358.  
  359.                     # if z in M then M is an elementary abelian extension  of
  360.                     # N  containing z, so  no  further conjugates need to  be
  361.                     # considered
  362.                     if z in M  then
  363.                         done := true;
  364.                     fi;
  365.                 fi;
  366.             od;
  367.  
  368.             # extend M with z, if still necessary 
  369.             if not done  then
  370.                 M := ClosureNormalizingElementPermGroup( M, z );
  371.                 AddSet( U, z );
  372.             fi;
  373.  
  374.             # store the conjugates of z
  375.             Append( Z, List( G.generators, g-> z^g ) );
  376.         fi;
  377.     od;
  378.  
  379.     if U <> [  ]  then
  380.  
  381.         # Now M/N is abelian, M=<N,U> and U contains elements of fixed order.
  382.         # Refine M>N to have  elementary abelian factors  by inserting powers
  383.         # of the generators in U, thereby extending N.
  384.         q := OrderFactorGroupElement( N, U[1] );
  385.         while not IsPrime(q)  do
  386.             q := q / FactorsInt(q)[1];
  387.             for u  in U  do
  388.                 if not u in N  then
  389.                     N := ClosureNormalizingElementPermGroup( N, u^q );
  390.                 fi;
  391.             od;  
  392.             G.elementaryAbelianSeries := Concatenation(
  393.                                              [N],
  394.                                              G.elementaryAbelianSeries );
  395.         od;
  396.  
  397.     # Now M/N is  elementary abelian. Extend the generator list  of M  to
  398.     # contain the generators of N.
  399.         M := N;
  400.         for v  in U  do
  401.         if not v in M  then
  402.             M := ClosureNormalizingElementPermGroup( M, v );
  403.         fi;
  404.         od;
  405.         G.elementaryAbelianSeries := Concatenation(
  406.                          [M],
  407.                          G.elementaryAbelianSeries );
  408.  
  409.     fi;
  410.  
  411.     return M;
  412.  
  413. end;
  414.  
  415.  
  416. #############################################################################
  417. ##
  418. #F  BaseStrongSubnormalGeneratingSetPPermGroup(<G>)  base and sgs for p-group
  419. ##
  420. ##  For intransitive <G>, this function  uses a  constituent  homomorphism to
  421. ##  get  a  base and  sgs  from  base  and  sgs for  kernel and  image.   For
  422. ##  imprimitive <G>, it likewise uses a blocks homomorphism to get a base and
  423. ##  sgs  from those of kernel  and  image. Finally,  if <G> is transitive and
  424. ##  primitive,  it must be isomorphic to <Z>_<p> so base and sgs are obvious.
  425. ##  The result  is bound to '<G>.polycyclicGenerators'  and  the  base can be
  426. ##  retrieved by 'Base(<G>)'.
  427. ##
  428. BaseStrongSubnormalGeneratingSetPPermGroup := function( G )
  429.     local   degree, pi, K, I, Delta, blocks, i, reps, rep, s, t, done;
  430.  
  431.     # if <G> is trivial return
  432.     if IsTrivial( G ) then
  433.     G.polycyclicGenerators := [  ];
  434.         InsertStabChain( G, [  ], [  ] );
  435.         return;
  436.     fi;
  437.  
  438.     # find a non-trivial orbit
  439.     degree := PermGroupOps.LargestMovedPoint( G );
  440.     i := 0;
  441.     repeat
  442.         i := i+1;
  443.         Delta := PermGroupOps.OrbitTransversal( G, i );
  444.     until 1 < Length(Delta.orbit);
  445.  
  446.     # <G> is intransitive
  447.     if Length(Delta.orbit)<degree then
  448.         pi := OperationHomomorphism( G, Operation(G,Delta.orbit) );
  449.         K  := Kernel(pi);
  450.         BaseStrongSubnormalGeneratingSetPPermGroup(K);
  451.         I := Image(pi);
  452.         BaseStrongSubnormalGeneratingSetPPermGroup(I);
  453.         G.polycyclicGenerators := Concatenation(
  454.             List( I.polycyclicGenerators,
  455.                   s -> PreImagesRepresentative(pi,s) ),
  456.             K.polycyclicGenerators );
  457.         InsertStabChain(
  458.             G,
  459.             Concatenation(List(Base(I),i->i^(pi.conperm^-1)),Base(K)),
  460.             G.polycyclicGenerators );
  461.  
  462.     # <G> is transitive
  463.     else
  464.         blocks := MaximalBlocks( G, [1..degree] );
  465.  
  466.         # <G> is imprimitive
  467.         if 1 < Length(blocks)  then
  468.             K := Stabilizer( G, blocks[1], OnSets );
  469.             BaseStrongSubnormalGeneratingSetPPermGroup(K);
  470.             i := First( G.generators, gen->not gen in K );
  471.             G.polycyclicGenerators := Concatenation(
  472.         [i],
  473.         K.polycyclicGenerators );
  474.             InsertStabChain( G, Base(K), G.polycyclicGenerators );
  475.  
  476.         # <G> is primitive, thus isomorphic to <Z>_<p>
  477.         else
  478.             G.polycyclicGenerators := [ G.generators[1] ];
  479.             InsertStabChain( G, [degree], G.polycyclicGenerators );
  480.         fi;
  481.     fi;
  482. end;
  483.  
  484.  
  485. #############################################################################
  486. ##
  487. #F  ExponentsPermSolvablePermGroup( <G>, <g> [, <start> ] ) . . . . . . . . .
  488. #F  . . . . . .  exponents of <g> as normal word in the pag generators of <G>
  489. ##
  490. ##  For  a  solvable  permutation  group <G> with bssgs  '<G>.bssgs' and  and
  491. ##  element  <g> in <G>, this function determines the  exponent vector  <exp>
  492. ##  such     that      '<g>     =     <G>.bssgs[<start>]^exp[<start>]     ...
  493. ##  <G>.bssgs[<n>]^exp[<n>]'.  If  a value  for  <start> is  known, it can be
  494. ##  given as third argument, otherwise it is defaulted to 1.
  495. ##
  496. ExponentsPermSolvablePermGroup := function( arg )
  497.     local  G,  g,  start,  exp,  eps,  h,  H,  pnt,  i;
  498.  
  499.     # Get the arguments.
  500.     G := arg[ 1 ];
  501.     g := arg[ 2 ];
  502.     if Length( arg ) > 2  then
  503.     start := arg[ 3 ];
  504.     else
  505.     start := 1;
  506.     fi;
  507.  
  508.     exp := [  ];
  509.  
  510.     # Mind the offset <start>.
  511.     for i  in [ start .. Length( G.bssgs ) ]  do
  512.  
  513.         # Find the base level of the <i>-th generator, remove the part of <g>
  514.     # not fixing the earlier basepoints.
  515.     h := g;
  516.          H := G;
  517.         while IsBound( H.orbit )  and  H.orbit[ 1 ] ^ G.bssgs[ i ] =
  518.                        H.orbit[ 1 ]  do
  519.         pnt := H.orbit[ 1 ];
  520.         while pnt ^ h <> pnt  do
  521.         h := h * H.transversal[ pnt^h ];
  522.         od;
  523.             H := H.stabilizer;
  524.       od;
  525.  
  526.         # Determine the <i>-th exponent.
  527.     eps := 0;
  528.         pnt := H.orbit[ 1 ] ^ h;
  529.     while H.transversal[ pnt ] = G.bssgs[ i ]  do
  530.         eps := eps + 1;
  531.         pnt := pnt / G.bssgs[ i ];
  532.     od;
  533.         exp[ i ] := eps;
  534.  
  535.     # Remove next factor of <g>.
  536.     g := G.bssgs[ i ] ^ eps mod g;
  537.  
  538.     od;
  539.  
  540.     # Return the result.
  541.     return exp;
  542.  
  543. end;
  544.     
  545. #############################################################################
  546. ##                        
  547. #F  PcPresentationPermGroup( <G>, <series>, <pgens>, <index>, <isNilp> )  . .
  548. ##
  549. ##  This function calculates a  pc presentation  for <G> along  a composition
  550. ##  series that  refines <series>.   <pgens> must contain the generator  list
  551. ##  according  to  the  composition  series  of  <G>.   <series>  can  be  an
  552. ##  elementary  abelian series, in which  case  <index> is a  list such  that
  553. ##  '<pgens>[<index[i]>]'  ...  '<pgens>[<index[i+1]>-1]' are the  generators
  554. ##  of the <i>-th elementary abelian factor in  <series>.  Otherwise <series>
  555. ##  must    equal    '<G>.compositionSeries'    and    <index>   must   equal
  556. ##  '[1..Length(<G>.compositionSeries)+1]'.
  557. ##
  558. ##  If <isNilp>  is true,  <series> must equal '<G>.compositionSeries'  which
  559. ##  must be a central series of <G>.  In this case, the composition series of
  560. ##  the resulting ag group is also a central series.
  561. ##
  562. ##  This     function    is    called    by    'PermGroupOps.AgGroup'     and
  563. ##  'PermGroupOps.PgGroup'.
  564. ##
  565. PcPresentationPermGroup := function( G, series, pgens, index, isNilp )
  566.     local   PC,  m,  p,  i,  i2,  n,  n2,  start,  k,  exp,  word,  rel;
  567.  
  568.     # <PC> will hold the presentation
  569.     m  := Length( pgens );
  570.     PC := rec( relations  := [],
  571.                generators := WordList( m, "g" ) );
  572.  
  573.     # Find the relations of the p-th powers. Use  the  vector space structure
  574.     # of the elementary abelian factors.
  575.     for i  in [ 1 .. Length(series)-1 ]  do
  576.         p := OrderFactorGroupElement(
  577.                  series[i+1],
  578.                  pgens[index[i]] );
  579.         for n  in [ index[i] .. index[i+1]-1 ]  do
  580.             word := PC.generators[n] ^ p;
  581.         rel  := word^0;
  582.         exp  := ExponentsPermSolvablePermGroup
  583.             ( G, pgens[n]^p, index[i+1] );
  584.             for k  in [ index[i+1] .. m ]  do
  585.         rel := rel * PC.generators[k]^exp[k];
  586.             od;
  587.             Add( PC.relations, word/rel );
  588.         od;
  589.     od;
  590.  
  591.     # Find the relations of the commutators.
  592.     for i  in [ 1 .. Length(series)-1 ]  do
  593.         for n  in [ index[i] .. index[i+1]-1 ]  do
  594.             for i2  in [ 1 .. i-1 ]  do
  595.                 if isNilp then
  596.                     start := n+1;
  597.                 else
  598.                     start := index[i2+1];
  599.                 fi;
  600.                 for n2  in [ index[i2] .. index[i2+1]-1 ]  do
  601.                     word := Comm( PC.generators[n], PC.generators[n2] );
  602.             rel  := word^0;
  603.               exp  := ExponentsPermSolvablePermGroup
  604.                 ( G, Comm( pgens[n], pgens[n2] ), start );
  605.                     for k  in [ start .. m ]  do
  606.             rel := rel * PC.generators[k]^exp[k];
  607.                     od;
  608.                     Add( PC.relations, word/rel );
  609.                 od;
  610.             od;
  611.             for n2  in [ index[i] .. n-1 ]  do
  612.                 word := Comm( PC.generators[n], PC.generators[n2] );
  613.         rel  := word^0;
  614.         exp  := ExponentsPermSolvablePermGroup
  615.                 ( G, Comm( pgens[n], pgens[n2] ), index[i+1] );
  616.                 for k  in [ index[i+1] .. m ]  do
  617.             rel := rel * PC.generators[k]^exp[k];
  618.                 od;
  619.                 Add( PC.relations, word/rel );
  620.             od;
  621.         od;
  622.     od;
  623.     return PC;
  624.  
  625. end;
  626.  
  627. #############################################################################
  628. ##
  629. #F  CompositionSeriesSolvablePermGroup( <G> ) . . . . . .  composition series
  630. ##
  631. CompositionSeriesSolvablePermGroup := function( G )
  632.     local  compositionSeries,  N,  elabstep,  i,  pgens,  g,  ord,  cof,
  633.            fac,  newgens, gen;
  634.  
  635.     # If  a subnormal series (with abelian factors) and polycyclic generators
  636.     # are  given, refine  the  series  to have <Z>_<p>-factors  by adding the
  637.     # polycyclic generators and their powers one by one.
  638.     if IsBound(G.subnormalSeries)  then
  639.         pgens             := G.polycyclicGenerators;
  640.     newgens           := [  ];
  641.         compositionSeries := [  ];
  642.         for i  in [ 1 .. Length(pgens) ]  do
  643.             N := G.subnormalSeries[i+1];
  644.             g := pgens[i];
  645.             ord := OrderFactorGroupElement( N, g );
  646.             cof := 1;
  647.             for fac  in FactorsInt(ord)  do
  648.                 if cof = 1  then
  649.                     Add( newgens, g );
  650.                     Add( compositionSeries, G.subnormalSeries[i] );
  651.                     g := g ^ fac;
  652.                 elif cof < ord  then
  653.                     Add( newgens, g );
  654.                     Add( compositionSeries,
  655.                          ClosureNormalizingElementPermGroup( N, g ) );
  656.                     g := g ^ fac;
  657.                 fi;
  658.                 cof := cof * fac;
  659.             od;
  660.         od;
  661.         Add( compositionSeries, TrivialSubgroup(G) );
  662.     G.polycyclicGenerators := newgens;
  663.  
  664.     else
  665.  
  666.     # Otherwise: First determine an elementary abelian series for  <G> in
  667.     # order to get a bssgs.
  668.     ElementaryAbelianSeries( G );
  669.  
  670.     # Start with the trivial subgroup.
  671.     N := TrivialSubgroup( G );
  672.     compositionSeries := [ N ];
  673.  
  674.     # Loop over the elementary abelian series.
  675.     for elabstep  in Reversed
  676.         ( [ 1 .. Length( G.elementaryAbelianSeries ) - 1 ] )  do
  677.  
  678.         # For  each elementary abelian factor,  add  in the  intermediate
  679.         # composition factors given by the pag system of <G>.
  680.         for gen  in Reversed( Sublist
  681.         ( G.elementaryAbelianSeries[ elabstep ].bssgs, [ 2 ..
  682.           Length( G.elementaryAbelianSeries[ elabstep   ].bssgs ) -
  683.               Length( G.elementaryAbelianSeries[ elabstep+1 ].bssgs ) ]
  684.                 ) )  do
  685.         N := ClosureNormalizingElementPermGroup( N, gen );
  686.         compositionSeries := Concatenation( [ N ], 
  687.                                  compositionSeries );
  688.         od;
  689.  
  690.         # Finally add in the subgroup from the elementary abelian series.
  691.         N := G.elementaryAbelianSeries[ elabstep ];
  692.         compositionSeries := Concatenation( [ N ], compositionSeries );
  693.  
  694.     od;
  695.  
  696.     fi;
  697.  
  698.     # Return the result.
  699.     return compositionSeries;
  700.  
  701. end;
  702.  
  703.  
  704. #############################################################################
  705. ##
  706. #F  SubnormalSeriesPPermGroup(<G>)  . . . . .  subnormal series for <p>-group
  707. ##
  708. ##  This function returns a subnormal series for <G>. A polycyclic generating
  709. ##  system for this series  that also contains strong generating sets for all
  710. ##  members of the subnormal series is bound to '<G>.polycyclicGenerators'.
  711. ##
  712. SubnormalSeriesPPermGroup := function( G )
  713.     local   subnormalSeries,  H,  gen;
  714.  
  715.     BaseStrongSubnormalGeneratingSetPPermGroup( G );
  716.     H := TrivialSubgroup(G);
  717.     InsertStabChain( H, Base(G), [  ] );
  718.     subnormalSeries := [ H ];
  719.     for gen in Reversed( Sublist( G.polycyclicGenerators,
  720.         [ 2 .. Length(G.polycyclicGenerators) ] ) )  do
  721.         H := ClosureNormalizingElementPermGroup( H, gen );
  722.         subnormalSeries := Concatenation( [H], subnormalSeries );
  723.     od;
  724.     subnormalSeries := Concatenation( [G], subnormalSeries );
  725.     return subnormalSeries;
  726.  
  727. end;
  728.  
  729.  
  730. #############################################################################
  731. ##
  732. #F  CentralCompositionSeriesPPermGroup( <G> ) . .  central composition series
  733. ##
  734. ##  This function calls 'SubnormalSeriesPPermGroup' to  calculate a subnormal
  735. ##  series with  strong generating  set and then improves it to be a  central
  736. ##  series maintaining the good properties of the generating set.  The method
  737. ##  used is known as Holt's algorithm.
  738. ##
  739. CentralCompositionSeriesPPermGroup := function( G )
  740.     local   words,  H,  gens,  base,  a,  f,  n,  i,  j,  h,  k,  l,  eps,
  741.         old_gen_k, list,  pgens,  tmp,  s,  p,  compositionSeries;
  742.  
  743.     # <G> must be a <p>-group
  744.     tmp := Set( FactorsInt( Size(G) ) );
  745.     if 1 < Length(tmp)  then
  746.         Error( "<G> must be a p-group" );
  747.     fi;
  748.     p := tmp[1];
  749.  
  750.     # calculate  a  base-adapted  subnormal  series   and   refine  it  to  a
  751.     # composition series
  752.     G.subnormalSeries := SubnormalSeriesPPermGroup(G);
  753.     compositionSeries := CompositionSeriesSolvablePermGroup(G);
  754.     base  := Base(G);
  755.     pgens := G.polycyclicGenerators;
  756.     n     := Length(pgens);
  757.  
  758.     # improve the composition series to a  p-central series  maintaining  the
  759.     # base-adaption (Holt's algorithm)
  760.     for i  in Reversed([1..n-2])  do
  761.         for j  in Reversed([i+2..n])  do
  762.             h := Comm( pgens[j], pgens[i] );
  763.             while not h in compositionSeries[j+1]  do
  764.                 f := First([i+1..n], x -> not h in compositionSeries[x+1]);
  765.         eps := 1;
  766.         h := pgens[f] mod h;
  767.         while not h in compositionSeries[ f+1 ]  do
  768.             eps := eps + 1;
  769.             h := pgens[f] mod h;
  770.         od;
  771.                 if eps > 1  then
  772.                     h := h^((1/eps) mod p);
  773.                 fi;
  774.                 k := f;
  775.                 for l  in [f+1..j]  do
  776.             eps := 0;
  777.                     while not h in compositionSeries[ l+1 ]  do
  778.             eps := eps + 1;
  779.             h := pgens[l] mod h;
  780.             od;
  781.                     if eps > 0  then
  782.                         old_gen_k := pgens[k];
  783.                         a := PositionProperty( base, b->b ^ pgens[l] <> b );
  784.                         tmp := pgens[k] * pgens[l]^eps;
  785.                         for s  in [ k+1 .. l ]  do
  786.                             pgens[s-1] := pgens[s];
  787.                         od;
  788.                         pgens[l] := tmp;
  789.                         if a <> false
  790.                            and ForAll(Sublist(base,[1..a]),b->b^old_gen_k=b)
  791.                         then
  792.                             pgens[l-1] := old_gen_k;
  793.                         fi;
  794.                         k := l;
  795.                     fi;
  796.                 od;
  797.                 if k <> j then
  798.                     tmp := pgens[k];
  799.                     for s  in [ k+1 .. j ]  do
  800.                         pgens[s-1] := pgens[s];
  801.                     od;
  802.                     pgens[j] := tmp;
  803.                 fi;
  804.                 for l  in [ f+1 .. j ]  do
  805.                     gens := Sublist( pgens, [l..n] );
  806.                     compositionSeries[l] := Subgroup( Parent(G), gens );
  807.                     InsertStabChain( compositionSeries[l], base, gens );
  808.                 od;
  809.                 h := Comm( pgens[j], pgens[i] );
  810.             od;
  811.         od;
  812.     od;
  813.  
  814.     return compositionSeries;
  815.  
  816. end;
  817.  
  818.  
  819. #############################################################################
  820. ##
  821.  
  822. #F  PermGroupOps.AgGroup(<G>) . . . . .  make an ag group out of a perm group
  823. ##
  824. PermGroupOps.AgGroup := function( G )
  825.     local   PC,  A,  series,  index;
  826.  
  827.     # Get an elementary abelian series and a bssgs of <G>. Find the positions
  828.     # in the bssgs where the generators of each factor in the series start.
  829.     series := ElementaryAbelianSeries( G );
  830.     index  := List( series, x -> Length( G.bssgs ) - Length( x.bssgs )+1 );
  831.  
  832.     # Calculate a pc presentation for <G>.
  833.     PC := PcPresentationPermGroup( G, series, G.bssgs, index, false );
  834.  
  835.     # Construct the ag group <A> and the bijection between <A> and <G>.
  836.     A := AgGroupFpGroup(PC);
  837.     A.bijection := GroupHomomorphismByImages(A,G,A.generators,G.bssgs);
  838.     A.bijection.isMapping           := true;
  839.     A.bijection.isGroupHomomorphism := true;
  840.     A.bijection.isInjective         := true;
  841.     A.bijection.isMonomorphism      := true;
  842.     A.bijection.isSurjective        := true;
  843.     A.bijection.isEpimorphism       := true;
  844.     A.bijection.isBijection         := true;
  845.     A.bijection.isIsomorphism       := true;
  846.     return A;
  847.  
  848. end;
  849.  
  850. #############################################################################
  851. ##
  852. #F  PermGroupOps.PgGroup(<G>) . . . . . . . .  pg group out of <p>-perm group
  853. ##
  854. PermGroupOps.PgGroup := function( G )
  855.     local   PC,  A,  series,  index;
  856.  
  857.     # Find a central series of <G>.
  858.     series := CentralCompositionSeriesPPermGroup( G );
  859.     index  := [ 1 .. Length( series )+1 ];
  860.  
  861.     # Calculate a pc presentation for <G>.
  862.     PC := PcPresentationPermGroup( G, series, G.polycyclicGenerators,
  863.                        index, true );
  864.  
  865.     # and construct the ag group <A> and the bijection between <A> and <G>
  866.     A := AgGroupFpGroup(PC);
  867.     A.bijection := GroupHomomorphismByImages(A,G,A.generators,
  868.                          G.polycyclicGenerators);
  869.     A.bijection.isMapping           := true;
  870.     A.bijection.isGroupHomomorphism := true;
  871.     A.bijection.isInjective         := true;
  872.     A.bijection.isMonomorphism      := true;
  873.     A.bijection.isSurjective        := true;
  874.     A.bijection.isEpimorphism       := true;
  875.     A.bijection.isBijection         := true;
  876.     A.bijection.isIsomorphism       := true;
  877.     return A;
  878.  
  879. end;
  880.  
  881.  
  882. #############################################################################
  883. ##
  884. #F  PermGroupOps.ElementaryAbelianSeries( <G> ) . . elementary abelian series
  885. ##
  886. PermGroupOps.ElementaryAbelianSeries := function( G )
  887.     if IsList(G)  then
  888.         return GroupOps.ElementaryAbelianSeries(G);
  889.     else
  890.         PermGroupOps.TryElementaryAbelianSeries(G);
  891.         return G.elementaryAbelianSeries;
  892.     fi;
  893. end;
  894.  
  895.  
  896. #############################################################################
  897. ##
  898. #F  PermGroupOps.IsSolvable( <G> )  . . . . . . . . . .  test for solvability
  899. ##
  900. ##  'PermGroupOps.IsSolvable' calls 'PermGroupOps.TryElementaryAbelianSeries'
  901. ##  and returns the flag '<G>.isSolvable' which is set by that function.
  902. ##
  903. PermGroupOps.IsSolvable := function( G )
  904.     PermGroupOps.TryElementaryAbelianSeries(G);
  905.     return G.isSolvable;
  906. end;
  907.  
  908.  
  909. #############################################################################
  910. ##
  911.  
  912. #E  Emacs . . . . . . . . . . . . . . . . . . . . . . . local emacs variables
  913. ##
  914. ##  Local Variables:
  915. ##  mode:           outline
  916. ##  outline-regexp: "#F\\|#V\\|#E"
  917. ##  fill-column:    77
  918. ##  fill-prefix:    "##  "
  919. ##  eval:           (hide-body)
  920. ##  End:
  921. ##
  922.