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

  1. #############################################################################
  2. ##
  3. #A  grpprods.g                  GAP library                      Frank Celler
  4. #A                                                         & Martin Schoenert
  5. ##
  6. #A  @(#)$Id: grpprods.g,v 3.12 1992/12/16 19:47:27 martin Rel $
  7. ##
  8. #Y  Copyright 1990-1992,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
  9. ##
  10. ##  This file contains functions for group products and group constructions.
  11. ##
  12. #H  $Log: grpprods.g,v $
  13. #H  Revision 3.12  1992/12/16  19:47:27  martin
  14. #H  replaced quoted record names with escaped ones
  15. #H
  16. #H  Revision 3.11  1992/03/27  11:14:51  martin
  17. #H  changed mapping to general mapping and function to mapping
  18. #H
  19. #H  Revision 3.10  1992/03/26  15:14:33  martin
  20. #H  changed 'SemiDirectProduct' to 'SemidirectProduct'
  21. #H
  22. #H  Revision 3.9  1992/03/19  15:42:13  martin
  23. #H  added basic groups
  24. #H
  25. #H  Revision 3.8  1992/02/10  15:14:35  martin
  26. #H  added the domain 'Mappings'
  27. #H
  28. #H  Revision 3.7  1992/01/29  09:09:38  martin
  29. #H  changed 'Order' to take two arguments, group and element
  30. #H
  31. #H  Revision 3.6  1992/01/07  12:39:57  martin
  32. #H  changed everything
  33. #H
  34. #H  Revision 3.5  1991/12/12  12:46:10  fceller
  35. #H  changed to use 'DirectProductElementOps', etc.
  36. #H
  37. #H  Revision 3.4  1991/11/07  09:36:15  fceller
  38. #H  More new names because of new domain concept.
  39. #H
  40. #H  Revision 3.3  1991/09/24  13:16:21  fceller
  41. #H  Minor improvment in 'SemiDirectProduct'.
  42. #H
  43. #H  Revision 3.2  1991/09/09  07:51:36  fceller
  44. #H  'SomethingAgGroup' is now 'AgGroupOps.Something'
  45. #H
  46. #H  Revision 3.1  1991/09/04  10:06:13  fceller
  47. #H  Initial release under RCS
  48. #H
  49. ##
  50.  
  51.  
  52. #############################################################################
  53. ##
  54. #F  InfoGroup1( <arg> ) . . . . . . . . . . . . . . . . . package information
  55. #F  InfoGroup2( <arg> ) . . . . . . . . . . . . . . package debug information
  56. ##
  57. if not IsBound( InfoGroup1 )  then InfoGroup1 := Ignore;  fi;
  58. if not IsBound( InfoGroup2 )  then InfoGroup2 := Ignore;  fi;
  59.  
  60.  
  61. #############################################################################
  62. ##
  63. #F  IsDirectProductElement(<obj>) . . . . . . . . . .  test if an object is a
  64. #F                                                     direct product element
  65. ##
  66. IsDirectProductElement := function ( obj )
  67.     return     IsRec( obj )
  68.            and IsBound( obj.isDirectProductElement )
  69.            and obj.isDirectProductElement;
  70. end;
  71.  
  72.  
  73. #############################################################################
  74. ##
  75. #F  DirectProductElement(<g>,..)  . . . . . . . . element of a direct product
  76. ##
  77. DirectProductElement := function ( arg )
  78.     local   D;
  79.     if Length( arg ) = 1  and IsList( arg[1] )  then
  80.         arg := arg[1];
  81.     fi;
  82.     D := Domain( arg );
  83.     return D.operations.DirectProductElement( D, arg );
  84. end;
  85.  
  86. GroupElementsOps.DirectProductElement := function ( D, elms )
  87.     local   elm;
  88.  
  89.     # make the direct product element
  90.     elm := rec( );
  91.     elm.isGroupElement          := true;
  92.     elm.isDirectProductElement  := true;
  93.     elm.domain                  := GroupElements;
  94.  
  95.     # enter the identifying information
  96.     elm.element                := ShallowCopy( elms );
  97.  
  98.     # enter the operations record
  99.     elm.operations              := DirectProductElementOps;
  100.  
  101.     # return the direct product element
  102.     return elm;
  103. end;
  104.  
  105. DirectProductElementOps         := Copy( GroupElementOps );
  106.  
  107. DirectProductElementOps.\*     := function ( a, b )
  108.     local   prd,        # product of <a> and <b>, result
  109.             i;          # loop variable
  110.  
  111.     # product of a direct product element
  112.     if IsDirectProductElement( a )  then
  113.  
  114.         # with another direct product element
  115.         if IsDirectProductElement( b )  then
  116.  
  117.             # check that they are compatible
  118.             if   a.domain <> b.domain  then
  119.                 Error("<a> and <b> must lie in the same domain");
  120.             elif Length(a.element) <> Length(b.element)  then
  121.                 Error("<a> and <b> must have the same number of elements");
  122.             fi;
  123.  
  124.             # compute the product
  125.             prd := rec();
  126.             prd.isGroupElement          := true;
  127.             prd.isDirectProductElement  := true;
  128.             prd.domain                  := a.domain;
  129.             prd.element                 := [];
  130.             for i  in [ 1 .. Length( a.element ) ]  do
  131.                 Add( prd.element, a.element[i] * b.element[i] );
  132.             od;
  133.             prd.operations              := a.operations;
  134.  
  135.         # with a list, distribute
  136.         elif IsList( b )  then
  137.             prd := [];
  138.             for i  in [ 1 .. Length( b ) ]  do
  139.                 prd[i] := a * b[i];
  140.             od;
  141.  
  142.         # with something else, undefined
  143.         else
  144.             Error("product of <a> and <b> must be defined");
  145.         fi;
  146.  
  147.     # of a list
  148.     elif IsList( a )  then
  149.  
  150.         # with a direct product element, distribute
  151.         if IsDirectProductElement( b )  then
  152.             prd := [];
  153.             for i  in [ 1 .. Length( a ) ]  do
  154.                 prd[i] := a[i] * b;
  155.             od;
  156.  
  157.         # with something else, undefined
  158.         else
  159.             Error("product of <a> and <b> must be defined");
  160.         fi;
  161.  
  162.     # maybe <a> knows how to handle this
  163.     elif    IsRec( a )
  164.         and IsBound( a.operations )
  165.         and IsBound( a.operations.\* )
  166.         and a.operations.\* <> DirectProductElementOps.\*
  167.     then
  168.         prd := a.operations.\*( a, b );
  169.  
  170.     # of something else is undefined
  171.     else
  172.         if IsDirectProductElement( b )  then
  173.             Error("product of <a> and <b> must be defined");
  174.         else
  175.             Error("panic: neither <a> nor <b> is a direct product element");
  176.         fi;
  177.     fi;
  178.  
  179.     # return the product
  180.     return prd;
  181. end;
  182.  
  183. DirectProductElementOps.\/     := function ( a, b )
  184.     local   quo,        # quotient of <a> and <b>, result
  185.             i;          # loop variable
  186.  
  187.     # quotient of a direct product element
  188.     if IsDirectProductElement( a )  then
  189.  
  190.         # with another direct product element
  191.         if IsDirectProductElement( b )  then
  192.  
  193.             # check that they are compatible
  194.             if   a.domain <> b.domain  then
  195.                 Error("<a> and <b> must lie in the same domain");
  196.             elif Length(a.element) <> Length(b.element)  then
  197.                 Error("<a> and <b> must have the same number of elements");
  198.             fi;
  199.  
  200.             # compute the quotient
  201.             quo := rec();
  202.             quo.isGroupElement          := true;
  203.             quo.isDirectProductElement  := true;
  204.             quo.domain                  := a.domain;
  205.             quo.element                 := [];
  206.             for i  in [ 1 .. Length( a.element ) ]  do
  207.                 Add( quo.element, a.element[i] / b.element[i] );
  208.             od;
  209.             quo.operations              := a.operations;
  210.  
  211.         # with a list, distribute
  212.         elif IsList( b )  then
  213.             quo := [];
  214.             for i  in [ 1 .. Length( b ) ]  do
  215.                 quo[i] := a / b[i];
  216.             od;
  217.  
  218.         # with something else, undefined
  219.         else
  220.             Error("quotient of <a> and <b> must be defined");
  221.         fi;
  222.  
  223.     # of a list
  224.     elif IsList( a )  then
  225.  
  226.         # with a direct product element, distribute
  227.         if IsDirectProductElement( b )  then
  228.             quo := [];
  229.             for i  in [ 1 .. Length( a ) ]  do
  230.                 quo[i] := a[i] / b;
  231.             od;
  232.  
  233.         # with something else, undefined
  234.         else
  235.             Error("quotient of <a> and <b> must be defined");
  236.         fi;
  237.  
  238.     # maybe <a> knows how to handle this
  239.     elif    IsRec( a )
  240.         and IsBound( a.operations )
  241.         and IsBound( a.operations.\/ )
  242.         and a.operations.\/ <> DirectProductElementOps.\/
  243.     then
  244.         quo := a.operations.\/( a, b );
  245.  
  246.     # of something else is undefined
  247.     else
  248.         if IsDirectProductElement( b )  then
  249.             Error("quotient of <a> and <b> must be defined");
  250.         else
  251.             Error("panic: neither <a> nor <b> is a direct product element");
  252.         fi;
  253.     fi;
  254.  
  255.     # return the quotient
  256.     return quo;
  257. end;
  258.  
  259. DirectProductElementOps.\^ := function ( a, b )
  260.     local   pow,        # power of <a> with <b>, result
  261.             i;          # loop variable
  262.  
  263.     # product of a direct product element
  264.     if IsDirectProductElement( a )  then
  265.  
  266.         # with another direct product element
  267.         if IsDirectProductElement( b )  then
  268.  
  269.             # check that they are compatible
  270.             if   a.domain <> b.domain  then
  271.                 Error("<a> and <b> must lie in the same domain");
  272.             elif Length(a.element) <> Length(b.element)  then
  273.                 Error("<a> and <b> must have the same number of elements");
  274.             fi;
  275.  
  276.             # compute the power
  277.             pow := rec();
  278.             pow.isGroupElement          := true;
  279.             pow.isDirectProductElement  := true;
  280.             pow.domain                  := a.domain;
  281.             pow.element                 := [];
  282.             for i  in [ 1 .. Length( a.element ) ]  do
  283.                 Add( pow.element, a.element[i] ^ b.element[i] );
  284.             od;
  285.             pow.operations              := a.operations;
  286.  
  287.         # with an integer
  288.         elif IsInt( b )  then
  289.  
  290.             # compute the product
  291.             pow := rec();
  292.             pow.isGroupElement          := true;
  293.             pow.isDirectProductElement  := true;
  294.             pow.domain                  := a.domain;
  295.             pow.element                 := [];
  296.             for i  in [ 1 .. Length( a.element ) ]  do
  297.                 Add( pow.element, a.element[i] ^ b );
  298.             od;
  299.             pow.operations              := a.operations;
  300.  
  301.         # with something else
  302.         else
  303.             Error("power of <a> and <b> must be defined");
  304.         fi;
  305.  
  306.     # maybe <a> knows how to handle this
  307.     elif    IsRec( a )
  308.         and IsBound( a.operations )
  309.         and IsBound( a.operations.\^ )
  310.         and a.operations.\^ <> DirectProductElementOps.\^
  311.     then
  312.         pow := a.operations.\^( a, b );
  313.  
  314.     # of something else is undefined
  315.     else
  316.         if IsDirectProductElement( b )  then
  317.             Error("power of <a> and <b> must be defined");
  318.         else
  319.             Error("panic: neither <a> nor <b> is a direct product element");
  320.         fi;
  321.     fi;
  322.  
  323.     # return the power
  324.     return pow;
  325. end;
  326.  
  327. DirectProductElementOps.Print := function ( a )
  328.     local   i;
  329.     Print( "DirectProductElement( " );
  330.     for i  in [ 1 .. Length( a.element )-1 ]  do
  331.         Print( a.element[i], ", " );
  332.     od;
  333.     Print( a.element[ Length(a.element) ], " )" );
  334. end;
  335.  
  336.  
  337. #############################################################################
  338. ##
  339. #F  IsDirectProduct(<D>)  . . . . . . . . test if a group is a direct product
  340. ##
  341. IsDirectProduct := function ( D )
  342.     return     IsRec( D )
  343.            and IsBound( D.isDirectProduct )
  344.            and D.isDirectProduct;
  345. end;
  346.  
  347.  
  348. #############################################################################
  349. ##
  350. #F  DirectProduct(<G>,..) . . . . . . . . . . . . .  direct product of groups
  351. ##
  352. DirectProduct := function ( arg )
  353.     local   D;          # direct product of the arguments, result
  354.  
  355.     # unravel the arguments
  356.     if Length( arg ) = 1  and IsList( arg[1] )  then
  357.         arg := arg[1];
  358.     fi;
  359.  
  360.     # check that the arguments are all groups
  361.     if Length( arg ) = 0  then
  362.         Error("usage: DirectProduct( <G>,.. )");
  363.     elif not ForAll( arg, IsGroup )  then
  364.         Error("<G1>, <G2>,.. must be groups");
  365.     fi;
  366.  
  367.     # make the direct product
  368.     D := arg[1].operations.DirectProduct( arg );
  369.  
  370.     # enter the relevant information
  371.     D.groups := ShallowCopy( arg );
  372.  
  373.     # return the direct product
  374.     return D;
  375. end;
  376.  
  377. GroupOps.DirectProduct := function ( grps )
  378.     local   D,          # direct product of <grps>, result
  379.             id,         # identity of <D>
  380.             gens,       # generators of <D>
  381.             gen,        # one generator of <D>
  382.             grp,        # one group in <grps>
  383.             i, j;       # loop variables
  384.  
  385.     # make the identity
  386.     id := rec( );
  387.     id.isGroupElement           := true;
  388.     id.isDirectProductElement   := true;
  389.     id.domain                   := GroupElements;
  390.     id.element                  := [];
  391.     for grp  in grps  do
  392.         Add( id.element, grp.identity );
  393.     od;
  394.     id.operations               := DirectProductElementOps;
  395.  
  396.     # make the set of generators
  397.     gens := [];
  398.     for i  in [ 1 .. Length( grps ) ]  do
  399.         for j  in [ 1 .. Length( grps[i].generators ) ]  do
  400.             gen := ShallowCopy( id );
  401.             gen.element := ShallowCopy( gen.element );
  402.             gen.element[i] := grps[i].generators[j];
  403.             Add( gens, gen );
  404.         od;
  405.     od;
  406.  
  407.     # make the direct product
  408.     D := Group( gens, id );
  409.  
  410.     # tag it as direct product
  411.     D.isDirectProduct   := true;
  412.  
  413.     # enter the information that relates to the construction
  414.     D.groups            := grps;
  415.  
  416.     # enter the operations record
  417.     D.operations        := DirectProductOps;
  418.  
  419.     # return the direct product
  420.     return D;
  421. end;
  422.  
  423.  
  424. #############################################################################
  425. ##
  426. #V  DirectProductOps  . . . . . . operations record for direct product groups
  427. ##
  428. DirectProductOps := Copy( GroupOps );
  429.  
  430. DirectProductOps.Size := function ( D )
  431.     return Product( List( D.groups, Size ) );
  432. end;
  433.  
  434. DirectProductOps.\in := function ( g, D )
  435.     return     IsDirectProductElement( g )
  436.            and IsDirectProduct( D )
  437.            and Length( g.element ) = Length( D.groups )
  438.            and ForAll( [ 1 .. Length( D.groups ) ],
  439.                        i -> g.element[i] in D.groups[i] );
  440. end;
  441.  
  442. DirectProductOps.Random := function ( D )
  443.     return DirectProductElement( List( D.groups, Random ) );
  444. end;
  445.  
  446. DirectProductOps.Centre := function ( D )
  447.     local   C,          # centre of <D>, result
  448.             i;          # loop variable
  449.  
  450.     # compute the centre as closure of the centres in the components
  451.     C := TrivialSubgroup( D );
  452.     for i  in [ 1 .. Length( D.groups ) ]  do
  453.         C := Closure( C,
  454.                       Image( Embedding( D.groups[i], D, i ),
  455.                              Centre( D.groups[i] ) ) );
  456.     od;
  457.  
  458.     # return the centre
  459.     return C;
  460. end;
  461.  
  462. DirectProductOps.SylowSubgroup := function ( D, p )
  463.     local   S,          # Sylow subgroup of <D>, result
  464.             i;          # loop variable
  465.  
  466.     # compute the Sylow as closure of the Sylows in the components
  467.     S := TrivialSubgroup( D );
  468.     for i  in [ 1 .. Length( D.groups ) ]  do
  469.         S := Closure( S,
  470.                       Image( Embedding( D.groups[i], D, i ),
  471.                              SylowSubgroup( D.groups[i], p ) ) );
  472.     od;
  473.  
  474.     # return the Sylow subgroup
  475.     return S;
  476. end;
  477.  
  478. DirectProductOps.Centralizer := function ( D, g )
  479.     local   C,          # centralizer of <g> in <D>, result
  480.             i;          # loop variable
  481.  
  482.     # compute the centralizer as closure of the centralizer in the components
  483.     C := TrivialSubgroup( D );
  484.     for i  in [ 1 .. Length( D.groups ) ]  do
  485.         C := Closure( C,
  486.                       Image( Embedding( D.groups[i], D, i ),
  487.                              Centralizer( D.groups[i],
  488.                                           Image( Projection(D,D.groups[i],i),
  489.                                                  g ) ) ) );
  490.     od;
  491.  
  492.     # return the centralizer
  493.     return C;
  494. end;
  495.  
  496. DirectProductOps.Order := function ( G, a )
  497.     local   ord,        # order of <a> in <G>, result
  498.             i;          # loop variable
  499.     ord := 1;
  500.     for i  in [ 1 .. Length( G.groups ) ]  do
  501.         ord := LcmInt( ord, Order( G.groups[i], a.element[i] ) );
  502.     od;
  503.     return ord;
  504. end;
  505.  
  506.  
  507. #############################################################################
  508. ##
  509. #F  DirectProductOps.Embedding(<G>,<D>,<i>) .  embedding of a component group
  510. #F                                                      into a direct product
  511. ##
  512. DirectProductOps.Embedding := function ( arg )
  513.     local   G,          # group <G>, first argument
  514.             D,          # direct product <D>, second argument
  515.             i,          # component, optional third argument
  516.             emb,        # embedding, result
  517.             k;          # loop variable
  518.  
  519.     # get the arguments
  520.     G := arg[1];
  521.     D := arg[2];
  522.     if Length( arg ) = 2  then
  523.         i := 0;
  524.         for k  in [ 1 .. Length( D.groups ) ]  do
  525.             if IsSubgroup( D.groups[k], G )  then
  526.                 if i <> 0  then
  527.                     Error("<G> appears in several components of <D>");
  528.                 fi;
  529.                 i := k;
  530.             fi;
  531.         od;
  532.     else
  533.         i := arg[3];
  534.     fi;
  535.  
  536.     # maybe we already know the embedding
  537.     if not IsBound( D.embeddings )  then
  538.         D.embeddings := [];
  539.     fi;
  540.     if not IsBound( D.embeddings[i] )  then
  541.         emb := rec();
  542.         emb.isGeneralMapping    := true;
  543.         emb.domain              := Mappings;
  544.         emb.source              := G;
  545.         emb.range               := D;
  546.         emb.isEmbedding         := true;
  547.         emb.component           := i;
  548.         emb.isMapping           := true;
  549.         emb.isInjective         := true;
  550.         emb.isHomomorphism      := true;
  551.         emb.kernel              := TrivialSubgroup( G );
  552.         emb.operations          := EmbeddingDirectProductOps;
  553.         D.embeddings[i]         := emb;
  554.     fi;
  555.  
  556.     # return the embedding
  557.     return D.embeddings[i];
  558. end;
  559.  
  560. EmbeddingDirectProductOps := Copy( GroupHomomorphismOps );
  561.  
  562. EmbeddingDirectProductOps.ImageElm := function ( emb, elm )
  563.     local   img;        # image of <elm> under <emb>, result
  564.  
  565.     # make the image
  566.     img := ShallowCopy( emb.range.identity );
  567.     img.element := ShallowCopy( img.element );
  568.     img.element[emb.component]  := elm;
  569.  
  570.     # return the image
  571.     return img;
  572. end;
  573.  
  574. EmbeddingDirectProductOps.ImagesElm := function ( emb, elm )
  575.     local   img;        # image of <elm> under <emb>, result
  576.  
  577.     # make the image
  578.     img := ShallowCopy( emb.range.identity );
  579.     img.element := ShallowCopy( img.element );
  580.     img.element[emb.component]  := elm;
  581.  
  582.     # return the image
  583.     return [ img ];
  584. end;
  585.  
  586. EmbeddingDirectProductOps.ImagesRepresentative := function ( emb, elm )
  587.     local   img;        # image of <elm> under <emb>, result
  588.  
  589.     # make the image
  590.     img := ShallowCopy( emb.range.identity );
  591.     img.element := ShallowCopy( img.element );
  592.     img.element[emb.component]  := elm;
  593.  
  594.     # return the image
  595.     return img;
  596. end;
  597.  
  598. EmbeddingDirectProductOps.PreImagesRepresentative := function ( emb, img )
  599.     local   elm;        # preimage of <elm> under <emb>, result
  600.  
  601.     # compute the preimage
  602.     elm := img.element[ emb.component ];
  603.  
  604.     # return the preimage
  605.     return elm;
  606. end;
  607.  
  608. EmbeddingDirectProductOps.Print := function ( emb )
  609.     Print("Embedding( ",emb.source,", ",emb.range,", ",emb.component," )");
  610. end;
  611.  
  612.  
  613. #############################################################################
  614. ##
  615. #F  DirectProductOps.Projection(<D>,<G>,<i>) . projection of a direct product
  616. #F                                                     onto a component group
  617. ##
  618. DirectProductOps.Projection := function ( arg )
  619.     local   D,          # direct product <D>, first argument
  620.             G,          # group <G>, second argument
  621.             i,          # component, optional third argument
  622.             prj,        # projection, result
  623.             k;          # loop variable
  624.  
  625.     # get the arguments
  626.     D := arg[1];
  627.     G := arg[2];
  628.     if Length( arg ) = 2  then
  629.         i := 0;
  630.         for k  in [ 1 .. Length( D.groups ) ]  do
  631.             if D.groups[i] = G  then
  632.                 if i <> 0  then
  633.                     Error("<G> is equal to several components of <D>");
  634.                 fi;
  635.                 i := k;
  636.             fi;
  637.         od;
  638.     else
  639.         i := arg[3];
  640.     fi;
  641.  
  642.     # maybe we already know the projection
  643.     if not IsBound( D.projections )  then
  644.         D.projections := [];
  645.     fi;
  646.     if not IsBound( D.projections[i] )  then
  647.         prj := rec();
  648.         prj.isGeneralMapping    := true;
  649.         prj.domain              := Mappings;
  650.         prj.source              := D;
  651.         prj.range               := G;
  652.         prj.isProjection        := true;
  653.         prj.component           := i;
  654.         prj.isMapping           := true;
  655.         prj.isSurjective        := true;
  656.         prj.isHomomorphism      := true;
  657.         prj.operations          := ProjectionDirectProductOps;
  658.         D.projections[i]        := prj;
  659.     fi;
  660.  
  661.     # return the projection
  662.     return D.projections[i];
  663. end;
  664.  
  665. ProjectionDirectProductOps := Copy( GroupHomomorphismOps );
  666.  
  667. ProjectionDirectProductOps.ImageElm := function ( prj, elm )
  668.     local   img;        # image of <elm> under <prj>, result
  669.  
  670.     # compute the image
  671.     img := elm.element[prj.component];
  672.  
  673.     # return the image
  674.     return img;
  675. end;
  676.  
  677. ProjectionDirectProductOps.ImagesElm := function ( prj, elm )
  678.     local   img;        # image of <elm> under <prj>, result
  679.  
  680.     # compute the image
  681.     img := elm.element[prj.component];
  682.  
  683.     # return the image
  684.     return [ img ];
  685. end;
  686.  
  687. ProjectionDirectProductOps.ImageRepresentative := function ( prj, elm )
  688.     local   img;        # image of <elm> under <prj>, result
  689.  
  690.     # compute the image
  691.     img := elm.element[prj.component];
  692.  
  693.     # return the image
  694.     return img;
  695. end;
  696.  
  697. ProjectionDirectProductOps.PreImagesRepresentative := function ( prj, img )
  698.     local   elm;        # preimage of <img> under <prj>, result
  699.  
  700.     # compute the preimage
  701.     elm := ShallowCopy( prj.source.identity );
  702.     elm.element := ShallowCopy( elm.element );
  703.     elm.element[prj.component]  := img;
  704.  
  705.     # return the preimage
  706.     return elm;
  707. end;
  708.  
  709. ProjectionDirectProductOps.Print := function ( prj )
  710.     Print("Projection( ",prj.source,", ",prj.range,", ",prj.component," )");
  711. end;
  712.  
  713.  
  714. #############################################################################
  715. ##
  716. #F  SubdirectProduct(<G1>,<G2>,<phi1>,<phi2>) . . subdirect product of groups
  717. ##
  718. SubdirectProduct := function ( G1, G2, phi1, phi2 )
  719.  
  720.     # check the arguments
  721.     if not IsGroup( G1 )  then
  722.         Error("<G1> must be a group");
  723.     elif not IsGroup( G2 )  then
  724.         Error("<G2> must be a group");
  725.     elif not IsGeneralMapping(phi1)  or not IsHomomorphism(phi1)  then
  726.         Error("<phi1> must be a homomorphism");
  727.     elif not IsGeneralMapping(phi2)  or not IsHomomorphism(phi2)  then
  728.         Error("<phi2> must be a homomorphism");
  729.     elif Image(phi1,G1) <> Image(phi2,G2)  then
  730.         Error("<G1> under <phi1> must be equal to <G2> under <phi2>");
  731.     fi;
  732.  
  733.     # dispatch depending on the first group
  734.     return G1.operations.SubdirectProduct( G1, G2, phi1, phi2 );
  735. end;
  736.  
  737. GroupOps.SubdirectProduct := function ( G1, G2, phi1, phi2 )
  738.     local   S,          # subdirect product of <G1> and <G2>, result
  739.             gens,       # generators of <S>
  740.             D,          # direct product of <G1> and <G2>
  741.             emb1, emb2, # embeddings of <G1> and <G2> into <D>
  742.             gen;        # one generator of <G1> or Kernel( <phi2> )
  743.  
  744.     # make the direct product and the embeddings
  745.     D := DirectProduct( G1, G2 );
  746.     emb1 := Embedding( G1, D, 1 );
  747.     emb2 := Embedding( G2, D, 2 );
  748.  
  749.     # the subdirect product is generated by $(g_1,x_{g_1})$ where $g_1$ loops
  750.     # over the  generators of $G_1$  and $x_{g_1} \in   G_2$ is abitrary such
  751.     # that $g_1^{phi_1} = x_{g_1}^{phi_2}$ and by $(1,k_2)$ where $k_2$ loops
  752.     # over the generators of the kernel of $phi_2$.
  753.     gens := [];
  754.     for gen  in G1.generators  do
  755.         Add( gens, gen^emb1 * PreImagesRepresentative(phi2,gen^phi1)^emb2 );
  756.     od;
  757.     for gen in Kernel( phi2 ).generators  do
  758.         Add( gens, gen ^ emb2 );
  759.     od;
  760.  
  761.     # and make the subdirect product
  762.     S := Group( D.operations.Subgroup( D, gens ) );
  763.     S.isSubdirectProduct        := true;
  764.  
  765.     # enter the information that relates to the construction
  766.     S.groups                    := [ G1, G2 ];
  767.     S.homomorphisms             := [ phi1, phi2 ];
  768.  
  769.     # enter the operations record
  770.     S.operations                := Copy( SubdirectProductOps );
  771.     S.operations.Projection     := D.operations.Projection;
  772.  
  773.     # return the subdirect product
  774.     return S;
  775. end;
  776.  
  777. SubdirectProductOps := Copy( GroupOps );
  778.  
  779. SubdirectProductOps.Size := function ( S )
  780.     return Size( S.groups[1] ) * Size( S.groups[2] )
  781.          / Size( Image( S.homomorphisms[1] ) );
  782. end;
  783.  
  784.  
  785. #############################################################################
  786. ##
  787. #F  IsSemidirectProductElement(<obj>) . . . . . . . .  test if an object is a
  788. #F                                                 semidirect product element
  789. ##
  790. IsSemidirectProductElement := function ( obj )
  791.     return     IsRec( obj )
  792.            and IsBound( obj.isSemidirectProductElement )
  793.            and obj.isSemidirectProductElement;
  794. end;
  795.  
  796.  
  797. #############################################################################
  798. ##
  799. #F  SemidirectProductElement(<g>,..)  . . . . element of a semidirect product
  800. ##
  801. SemidirectProductElement := function ( g, a, h )
  802.     local   D;
  803.     D := Domain( [ g, h ] );
  804.     return D.operations.SemidirectProductElement( D, g, a, h );
  805. end;
  806.  
  807. GroupElementsOps.SemidirectProductElement := function ( D, g, a, h )
  808.     local   elm;
  809.  
  810.     # make the semidirect product element
  811.     elm := rec( );
  812.     elm.isGroupElement              := true;
  813.     elm.isSemidirectProductElement  := true;
  814.     elm.domain                      := GroupElements;
  815.  
  816.     # enter the identifying information
  817.     elm.element                     := [ g, h ];
  818.     elm.automorphism                := a;
  819.  
  820.     # enter the operations record
  821.     elm.operations                  := SemidirectProductElementOps;
  822.  
  823.     # return the semidirect product element
  824.     return elm;
  825. end;
  826.  
  827. SemidirectProductElementOps         := Copy( GroupElementOps );
  828.  
  829. SemidirectProductElementOps.\*     := function ( a, b )
  830.     local   prd,        # product of <a> and <b>, result
  831.             i;          # loop variable
  832.  
  833.     # product of a semidirect product element
  834.     if IsSemidirectProductElement( a )  then
  835.  
  836.         # with another semidirect product element
  837.         if IsSemidirectProductElement( b )  then
  838.  
  839.             # check that they are compatible
  840.             if   a.domain <> b.domain  then
  841.                 Error("<a> and <b> must lie in the same domain");
  842.             fi;
  843.  
  844.             # compute the product
  845.             prd := rec();
  846.             prd.isGroupElement  := true;
  847.             prd.isSemidirectProductElement := true;
  848.             prd.domain          := a.domain;
  849.             prd.automorphism    := a.automorphism * b.automorphism;
  850.             prd.element         := [ a.element[1]
  851.                                     *b.element[1],
  852.                                      a.element[2]^b.automorphism
  853.                                     *b.element[2] ];
  854.             prd.operations      := a.operations;
  855.  
  856.         # with a list, distribute
  857.         elif IsList( b )  then
  858.             prd := [];
  859.             for i  in [ 1 .. Length( b ) ]  do
  860.                 prd[i] := a * b[i];
  861.             od;
  862.  
  863.         # with something else, undefined
  864.         else
  865.             Error("product of <a> and <b> must be defined");
  866.         fi;
  867.  
  868.     # of a list
  869.     elif IsList( a )  then
  870.  
  871.         # with a semidirect product element, distribute
  872.         if IsSemidirectProductElement( b )  then
  873.             prd := [];
  874.             for i  in [ 1 .. Length( a ) ]  do
  875.                 prd[i] := a[i] * b;
  876.             od;
  877.  
  878.         # with something else, undefined
  879.         else
  880.             Error("product of <a> and <b> must be defined");
  881.         fi;
  882.  
  883.     # maybe <a> knows how to handle this
  884.     elif    IsRec( a )
  885.         and IsBound( a.operations )
  886.         and IsBound( a.operations.\* )
  887.         and a.operations.\* <> SemidirectProductElementOps.\*
  888.     then
  889.         prd := a.operations.\*( a, b );
  890.  
  891.     # of something else is undefined
  892.     else
  893.         if IsSemidirectProductElement( b )  then
  894.          Error("product of <a> and <b> must be defined");
  895.         else
  896.          Error("panic: neither <a> nor <b> is a semidirect product element");
  897.         fi;
  898.     fi;
  899.  
  900.     # return the product
  901.     return prd;
  902. end;
  903.  
  904. SemidirectProductElementOps.\^ := function ( a, b )
  905.     local   pow,        # power of <a> with <b>, result
  906.             i;          # loop variable
  907.  
  908.     # product of a semidirect product element
  909.     if IsSemidirectProductElement( a )  then
  910.  
  911.         # with another semidirect product element
  912.         if IsSemidirectProductElement( b )  then
  913.  
  914.             # check that they are compatible
  915.             if   a.domain <> b.domain  then
  916.                 Error("<a> and <b> must lie in the same domain");
  917.             fi;
  918.  
  919.             # compute the power
  920.             pow := rec();
  921.             pow.isGroupElement  := true;
  922.             pow.isSemidirectProductElement := true;
  923.             pow.domain          := a.domain;
  924.             pow.automorphism    := a.automorphism ^ b.automorphism;
  925.             pow.element         := [ a.element[1]
  926.                                     ^b.element[1],
  927.                                     (b.element[2]^-1)^pow.automorphism
  928.                                     *a.element[2]^b.automorphism
  929.                                     *b.element[2] ];
  930.             pow.operations      := a.operations;
  931.  
  932.         # with an integer
  933.         elif IsInt( b )  then
  934.  
  935.             # if necessary invert the element
  936.             if b < 0  then
  937.                 pow := ShallowCopy( a );
  938.                 pow.automorphism  := a.automorphism^-1;
  939.                 pow.element       := [ a.element[1]^-1,
  940.                                       (a.element[2]^-1)^a.automorphism ];
  941.                 a := pow;
  942.                 b := -b;
  943.             fi;
  944.  
  945.             # make the identity element
  946.             pow := rec();
  947.             pow.isGroupElement  := true;
  948.             pow.isSemidirectProductElement := true;
  949.             pow.domain          := a.domain;
  950.             pow.automorphism    := a.automorphism^0;
  951.             pow.element         := [ a.element[1]^0,
  952.                                      a.element[2]^0 ];
  953.             pow.operations      := a.operations;
  954.                 
  955.             # use repreated squaring method
  956.             if b <> 0  then
  957.                 i := 2 ^ ( LogInt( b, 2 ) + 1 );
  958.                 while 1 < i  do
  959.                     pow := pow * pow;
  960.                     i := QuoInt( i, 2 );
  961.                     if i <= b  then
  962.                         pow := pow * a;
  963.                         b := b - i;
  964.                     fi;
  965.                 od;
  966.             fi;
  967.  
  968.         # with something else
  969.         else
  970.             Error("power of <a> and <b> must be defined");
  971.         fi;
  972.  
  973.     # maybe <a> knows how to handle this
  974.     elif    IsRec( a )
  975.         and IsBound( a.operations )
  976.         and IsBound( a.operations.\^ )
  977.         and a.operations.\^ <> SemidirectProductElementOps.\^
  978.     then
  979.         pow := a.operations.\^( a, b );
  980.  
  981.     # of something else is undefined
  982.     else
  983.         if IsSemidirectProductElement( b )  then
  984.          Error("power of <a> and <b> must be defined");
  985.         else
  986.          Error("panic: neither <a> nor <b> is a semidirect product element");
  987.         fi;
  988.     fi;
  989.  
  990.     # return the power
  991.     return pow;
  992. end;
  993.  
  994. SemidirectProductElementOps.Print := function ( a )
  995.     Print( "SemidirectProductElement( ", a.element[1], ", ",
  996.            a.automorphism, ", ", a.element[2], " )" );
  997. end;
  998.  
  999.  
  1000. #############################################################################
  1001. ##
  1002. #F  IsSemidirectProduct(<D>)  . . . . test if a group is a semidirect product
  1003. ##
  1004. IsSemidirectProduct := function ( D )
  1005.     return     IsRec( D )
  1006.            and IsBound( D.isSemidirectProduct )
  1007.            and D.isSemidirectProduct;
  1008. end;
  1009.  
  1010.  
  1011. #############################################################################
  1012. ##
  1013. #F  SemidirectProduct(<G>,<a>,<H>)  . . . . . .  semidirect product of groups
  1014. ##
  1015. SemidirectProduct := function ( G, a, H )
  1016.     local   D;          # semidirect product of the arguments, result
  1017.  
  1018.     # check that the arguments are all groups
  1019.     if not IsGroup( G )  or not IsGroup( H )  then
  1020.         Error("<G> and <H> must be groups");
  1021.     fi;
  1022.  
  1023.     # make the semidirect product
  1024.     D := G.operations.SemidirectProduct( G, a, H );
  1025.  
  1026.     # enter the relevant information
  1027.     D.groups := [ G, H ];
  1028.     D.mapping := a;
  1029.  
  1030.     # return the semidirect product
  1031.     return D;
  1032. end;
  1033.  
  1034. GroupOps.SemidirectProduct := function ( G, a, H )
  1035.     local   D,          # semidirect product of <grps>, result
  1036.             id,         # identity of <D>
  1037.             gens,       # generators of <D>
  1038.             gen,        # one generator of <D>
  1039.             grp,        # one group in <grps>
  1040.             i, j;       # loop variables
  1041.  
  1042.     # make the identity
  1043.     id := rec( );
  1044.     id.isGroupElement   := true;
  1045.     id.isSemidirectProductElement := true;
  1046.     id.domain           := GroupElements;
  1047.     id.element          := [ G.identity, H.identity ];
  1048.     id.automorphism     := G.identity ^ a;
  1049.     id.operations       := SemidirectProductElementOps;
  1050.  
  1051.     # make the set of generators
  1052.     gens := [];
  1053.     for j  in [ 1 .. Length( G.generators ) ]  do
  1054.         gen := ShallowCopy( id );
  1055.         gen.element      := [ G.generators[j], H.identity ];
  1056.         gen.automorphism := G.generators[j] ^ a;
  1057.         Add( gens, gen );
  1058.     od;
  1059.     for j  in [ 1 .. Length( H.generators ) ]  do
  1060.         gen := ShallowCopy( id );
  1061.         gen.element      := [ G.identity, H.generators[j] ];
  1062.         Add( gens, gen );
  1063.     od;
  1064.  
  1065.     # make the semidirect product
  1066.     D := Group( gens, id );
  1067.  
  1068.     # tag it as semidirect product
  1069.     D.isSemidirectProduct       := true;
  1070.  
  1071.     # enter the information that relates to the construction
  1072.     D.groups                    := [ G, H ];
  1073.     D.mapping                   := a;
  1074.  
  1075.     # enter the operations record
  1076.     D.operations                := SemidirectProductOps;
  1077.  
  1078.     # return the semidirect product
  1079.     return D;
  1080. end;
  1081.  
  1082.  
  1083. #############################################################################
  1084. ##
  1085. #V  SemidirectProductOps  . . operations record for semidirect product groups
  1086. ##
  1087. SemidirectProductOps := Copy( GroupOps );
  1088.  
  1089. SemidirectProductOps.Size := function ( D )
  1090.     return Product( List( D.groups, Size ) );
  1091. end;
  1092.  
  1093. SemidirectProductOps.\in := function ( g, D )
  1094.     return     IsSemidirectProductElement( g )
  1095.            and IsSemidirectProduct( D )
  1096.            and g.element[1] in D.groups[1]
  1097.            and g.element[2] in D.groups[2];
  1098. end;
  1099.  
  1100. SemidirectProductOps.Random := function ( D )
  1101.     local   g, h;
  1102.     g := Random( D.groups[1] );
  1103.     h := Random( D.groups[2] );
  1104.     return SemidirectProductElement( g, g ^ D.mapping, h );
  1105. end;
  1106.  
  1107.  
  1108. #############################################################################
  1109. ##
  1110. #F  SemidirectProductOps.Embedding(<G>,<D>,<i>) . .  embedding of a component
  1111. #F                                            group into a semidirect product
  1112. ##
  1113. SemidirectProductOps.Embedding := function ( arg )
  1114.     local   G,          # group <G>, first argument
  1115.             D,          # direct product <D>, second argument
  1116.             i,          # component, optional third argument
  1117.             emb,        # embedding, result
  1118.             k;          # loop variable
  1119.  
  1120.     # get the arguments
  1121.     G := arg[1];
  1122.     D := arg[2];
  1123.     if Length( arg ) = 2  then
  1124.         i := 0;
  1125.         for k  in [ 1 .. Length( D.groups ) ]  do
  1126.             if IsSubgroup( D.groups[k], G )  then
  1127.                 if i <> 0  then
  1128.                     Error("<G> appears in several components of <D>");
  1129.                 fi;
  1130.                 i := k;
  1131.             fi;
  1132.         od;
  1133.     else
  1134.         i := arg[3];
  1135.     fi;
  1136.  
  1137.     # maybe we already know the embedding
  1138.     if not IsBound( D.embeddings )  then
  1139.         D.embeddings := [];
  1140.     fi;
  1141.     if not IsBound( D.embeddings[i] )  then
  1142.         emb := rec();
  1143.         emb.isGeneralMapping    := true;
  1144.         emb.domain              := Mappings;
  1145.         emb.source              := G;
  1146.         emb.range               := D;
  1147.         emb.isEmbedding         := true;
  1148.         emb.component           := i;
  1149.         emb.isMapping           := true;
  1150.         emb.isInjective         := true;
  1151.         emb.isHomomorphism      := true;
  1152.         emb.kernel              := TrivialSubgroup( G );
  1153.         emb.operations          := EmbeddingSemidirectProductOps;
  1154.         D.embeddings[i]         := emb;
  1155.     fi;
  1156.  
  1157.     # return the embedding
  1158.     return D.embeddings[i];
  1159. end;
  1160.  
  1161. EmbeddingSemidirectProductOps := Copy( GroupHomomorphismOps );
  1162.  
  1163. EmbeddingSemidirectProductOps.ImageElm := function ( emb, elm )
  1164.     local   img;        # image of <elm> under <emb>, result
  1165.  
  1166.     # make the image
  1167.     img := ShallowCopy( emb.range.identity );
  1168.     img.element := ShallowCopy( img.element );
  1169.     img.element[emb.component]  := elm;
  1170.     if emb.component = 1  then
  1171.         img.automorphism := elm ^ emb.range.mapping;
  1172.     fi;
  1173.  
  1174.     # return the image
  1175.     return img;
  1176. end;
  1177.  
  1178. EmbeddingSemidirectProductOps.ImagesElm := function ( emb, elm )
  1179.     local   img;        # image of <elm> under <emb>, result
  1180.  
  1181.     # make the image
  1182.     img := ShallowCopy( emb.range.identity );
  1183.     img.element := ShallowCopy( img.element );
  1184.     img.element[emb.component]  := elm;
  1185.     if emb.component = 1  then
  1186.         img.automorphism := elm ^ emb.range.mapping;
  1187.     fi;
  1188.  
  1189.     # return the image
  1190.     return [ img ];
  1191. end;
  1192.  
  1193. EmbeddingSemidirectProductOps.ImagesRepresentative := function ( emb, elm )
  1194.     local   img;        # image of <elm> under <emb>, result
  1195.  
  1196.     # make the image
  1197.     img := ShallowCopy( emb.range.identity );
  1198.     img.element := ShallowCopy( img.element );
  1199.     img.element[emb.component]  := elm;
  1200.     if emb.component = 1  then
  1201.         img.automorphism := elm ^ emb.range.mapping;
  1202.     fi;
  1203.  
  1204.     # return the image
  1205.     return img;
  1206. end;
  1207.  
  1208. EmbeddingSemidirectProductOps.PreImagesRepresentative := function (emb,img)
  1209.     local   elm;        # preimage of <elm> under <emb>, result
  1210.  
  1211.     # compute the preimage
  1212.     elm := img.element[ emb.component ];
  1213.  
  1214.     # return the preimage
  1215.     return elm;
  1216. end;
  1217.  
  1218. EmbeddingSemidirectProductOps.Print := function ( emb )
  1219.     Print("Embedding( ",emb.source,", ",emb.range,", ",emb.component," )");
  1220. end;
  1221.  
  1222.  
  1223. #############################################################################
  1224. ##
  1225. #F  SemidirectProductOps.Projection(<D>,<G>,<i>) . projection of a semidirect
  1226. #F                                             product onto a component group
  1227. ##
  1228. SemidirectProductOps.Projection := function ( arg )
  1229.     local   D,          # direct product <D>, first argument
  1230.             G,          # group <G>, second argument
  1231.             i,          # component, optional third argument
  1232.             prj,        # projection, result
  1233.             k;          # loop variable
  1234.  
  1235.     # get the arguments
  1236.     D := arg[1];
  1237.     G := arg[2];
  1238.     if Length( arg ) = 2  then
  1239.         i := 0;
  1240.         for k  in [ 1 .. Length( D.groups ) ]  do
  1241.             if D.groups[i] = G  then
  1242.                 if i <> 0  then
  1243.                     Error("<G> is equal to several components of <D>");
  1244.                 fi;
  1245.                 i := k;
  1246.             fi;
  1247.         od;
  1248.     else
  1249.         i := arg[3];
  1250.     fi;
  1251.  
  1252.     # maybe we already know the projection
  1253.     if not IsBound( D.projections )  then
  1254.         D.projections := [];
  1255.     fi;
  1256.     if not IsBound( D.projections[i] )  then
  1257.         prj := rec();
  1258.         prj.isGeneralMapping    := true;
  1259.         prj.domain              := Mappings;
  1260.         prj.source              := D;
  1261.         prj.range               := G;
  1262.         prj.isProjection        := true;
  1263.         prj.component           := i;
  1264.         prj.isMapping           := true;
  1265.         prj.isSurjective        := true;
  1266.         prj.isHomomorphism      := true;
  1267.         prj.operations          := ProjectionSemidirectProductOps;
  1268.         D.projections[i]        := prj;
  1269.     fi;
  1270.  
  1271.     # return the projection
  1272.     return D.projections[i];
  1273. end;
  1274.  
  1275. ProjectionSemidirectProductOps := Copy( GroupHomomorphismOps );
  1276.  
  1277. ProjectionSemidirectProductOps.ImageElm := function ( prj, elm )
  1278.     local   img;        # image of <elm> under <prj>, result
  1279.  
  1280.     # compute the image
  1281.     img := elm.element[prj.component];
  1282.  
  1283.     # return the image
  1284.     return img;
  1285. end;
  1286.  
  1287. ProjectionSemidirectProductOps.ImagesElm := function ( prj, elm )
  1288.     local   img;        # image of <elm> under <prj>, result
  1289.  
  1290.     # compute the image
  1291.     img := elm.element[prj.component];
  1292.  
  1293.     # return the image
  1294.     return [ img ];
  1295. end;
  1296.  
  1297. ProjectionSemidirectProductOps.ImageRepresentative := function ( prj, elm )
  1298.     local   img;        # image of <elm> under <prj>, result
  1299.  
  1300.     # compute the image
  1301.     img := elm.element[prj.component];
  1302.  
  1303.     # return the image
  1304.     return img;
  1305. end;
  1306.  
  1307. ProjectionSemidirectProductOps.PreImagesRepresentative := function (prj,img)
  1308.     local   elm;        # preimage of <img> under <prj>, result
  1309.  
  1310.     # compute the preimage
  1311.     elm := ShallowCopy( prj.source.identity );
  1312.     elm.element := ShallowCopy( elm.element );
  1313.     elm.element[prj.component]  := img;
  1314.  
  1315.     # return the preimage
  1316.     return elm;
  1317. end;
  1318.  
  1319. ProjectionSemidirectProductOps.Print := function ( prj )
  1320.     Print("Projection( ",prj.source,", ",prj.range,", ",prj.component," )");
  1321. end;
  1322.  
  1323.  
  1324. #############################################################################
  1325. ##
  1326. #F  IsWreathProductElement(<obj>) . . . . . . . . . .  test if an object is a
  1327. #F                                                     wreath product element
  1328. ##
  1329. IsWreathProductElement := function ( obj )
  1330.     return     IsRec( obj )
  1331.            and IsBound( obj.isWreathProductElement )
  1332.            and obj.isWreathProductElement;
  1333. end;
  1334.  
  1335.  
  1336. #############################################################################
  1337. ##
  1338. #F  WreathProductElement(<n1>,<n2>...,<g>,<p>)  . element of a wreath product
  1339. ##
  1340. WreathProductElement := function ( arg )
  1341.     local   D;
  1342.     D := Domain( [ arg[1] ] );
  1343.     return D.operations.WreathProductElement( D,
  1344.                                 Sublist( arg, [1..Length(arg)-2] ),
  1345.                                 arg[Length(arg)-1], arg[Length(arg)] );
  1346. end;
  1347.  
  1348. GroupElementsOps.WreathProductElement := function ( D, elms, g, p )
  1349.     local   elm;
  1350.  
  1351.     # make the wreath product element
  1352.     elm := rec( );
  1353.     elm.isGroupElement              := true;
  1354.     elm.isWreathProductElement      := true;
  1355.     elm.domain                      := GroupElements;
  1356.  
  1357.     # enter the identifying information
  1358.     elm.element                     := Concatenation( elms, [g] );
  1359.     elm.permutation                 := p;
  1360.  
  1361.     # enter the operations record
  1362.     elm.operations                  := WreathProductElementOps;
  1363.  
  1364.     # return the wreath product element
  1365.     return elm;
  1366. end;
  1367.  
  1368. WreathProductElementOps         := Copy( GroupElementOps );
  1369.  
  1370. WreathProductElementOps.\*     := function ( a, b )
  1371.     local   prd,        # product of <a> and <b>, result
  1372.             i;          # loop variable
  1373.  
  1374.     # product of a wreath product element
  1375.     if IsWreathProductElement( a )  then
  1376.  
  1377.         # with another wreath product element
  1378.         if IsWreathProductElement( b )  then
  1379.  
  1380.             # check that they are compatible
  1381.             if   a.domain <> b.domain  then
  1382.                 Error("<a> and <b> must lie in the same domain");
  1383.             elif Length(a.element) <> Length(b.element)  then
  1384.                 Error("<a> and <b> must have the same number of element");
  1385.             fi;
  1386.  
  1387.             # compute the product
  1388.             prd := rec();
  1389.             prd.isGroupElement  := true;
  1390.             prd.isWreathProductElement := true;
  1391.             prd.domain          := a.domain;
  1392.             prd.element         := [];
  1393.             for i  in [1..Length(a.element)-1]  do
  1394.                 prd.element[i]  := a.element[i] * b.element[i^a.permutation];
  1395.             od;
  1396.             i := Length(a.element);
  1397.             prd.element[i]      := a.element[i]  * b.element[i];
  1398.             prd.permutation     := a.permutation * b.permutation;
  1399.             prd.operations      := a.operations;
  1400.  
  1401.         # with a list, distribute
  1402.         elif IsList( b )  then
  1403.             prd := [];
  1404.             for i  in [ 1 .. Length( b ) ]  do
  1405.                 prd[i] := a * b[i];
  1406.             od;
  1407.  
  1408.         # with something else, undefined
  1409.         else
  1410.             Error("product of <a> and <b> must be defined");
  1411.         fi;
  1412.  
  1413.     # of a list
  1414.     elif IsList( a )  then
  1415.  
  1416.         # with a wreath product element, distribute
  1417.         if IsWreathProductElement( b )  then
  1418.             prd := [];
  1419.             for i  in [ 1 .. Length( a ) ]  do
  1420.                 prd[i] := a[i] * b;
  1421.             od;
  1422.  
  1423.         # with something else, undefined
  1424.         else
  1425.             Error("product of <a> and <b> must be defined");
  1426.         fi;
  1427.  
  1428.     # maybe <a> knows how to handle this
  1429.     elif    IsRec( a )
  1430.         and IsBound( a.operations )
  1431.         and IsBound( a.operations.\* )
  1432.         and a.operations.\* <> WreathProductElementOps.\*
  1433.     then
  1434.         prd := a.operations.\*( a, b );
  1435.  
  1436.     # of something else is undefined
  1437.     else
  1438.         if IsWreathProductElement( b )  then
  1439.          Error("product of <a> and <b> must be defined");
  1440.         else
  1441.          Error("panic: neither <a> nor <b> is a wreath product element");
  1442.         fi;
  1443.     fi;
  1444.  
  1445.     # return the product
  1446.     return prd;
  1447. end;
  1448.  
  1449. WreathProductElementOps.\^ := function ( a, b )
  1450.     local   pow,        # power of <a> with <b>, result
  1451.             i;          # loop variable
  1452.  
  1453.     # product of a wreath product element
  1454.     if IsWreathProductElement( a )  then
  1455.  
  1456.         # with another wreath product element
  1457.         if IsWreathProductElement( b )  then
  1458.  
  1459.             # check that they are compatible
  1460.             if   a.domain <> b.domain  then
  1461.                 Error("<a> and <b> must lie in the same domain");
  1462.             fi;
  1463.  
  1464.             # compute the power
  1465.             pow := b^-1 * a * b;
  1466.  
  1467.         # with an integer
  1468.         elif IsInt( b )  then
  1469.  
  1470.             # if necessary invert the element
  1471.             if b < 0  then
  1472.                 pow := ShallowCopy( a );
  1473.                 pow.element     := [];
  1474.                 for i  in [1..Length(a.element)-1]  do
  1475.                     pow.element[i^a.permutation] := a.element[i]^-1;
  1476.                 od;
  1477.                 i := Length(a.element);
  1478.                 pow.element[i]  := a.element[i]^-1;
  1479.                 pow.permutation := a.permutation^-1;
  1480.                 a := pow;
  1481.                 b := -b;
  1482.             fi;
  1483.  
  1484.             # make the identity element
  1485.             pow := rec();
  1486.             pow.isGroupElement  := true;
  1487.             pow.isWreathProductElement := true;
  1488.             pow.domain          := a.domain;
  1489.             pow.element         := [];
  1490.             for i  in [1..Length(a.element)-1]  do
  1491.                 pow.element[i]  := a.element[i]^0;
  1492.             od;
  1493.             i := Length(a.element);
  1494.             pow.element[i]      := a.element[i]^0;
  1495.             pow.permutation     := ();
  1496.             pow.operations      := a.operations;
  1497.                 
  1498.             # use repreated squaring method
  1499.             if b <> 0  then
  1500.                 i := 2 ^ ( LogInt( b, 2 ) + 1 );
  1501.                 while 1 < i  do
  1502.                     pow := pow * pow;
  1503.                     i := QuoInt( i, 2 );
  1504.                     if i <= b  then
  1505.                         pow := pow * a;
  1506.                         b := b - i;
  1507.                     fi;
  1508.                 od;
  1509.             fi;
  1510.  
  1511.         # with something else
  1512.         else
  1513.             Error("power of <a> and <b> must be defined");
  1514.         fi;
  1515.  
  1516.     # maybe <a> knows how to handle this
  1517.     elif    IsRec( a )
  1518.         and IsBound( a.operations )
  1519.         and IsBound( a.operations.\^ )
  1520.         and a.operations.\^ <> WreathProductElementOps.\^
  1521.     then
  1522.         pow := a.operations.\^( a, b );
  1523.  
  1524.     # of something else is undefined
  1525.     else
  1526.         if IsWreathProductElement( b )  then
  1527.          Error("power of <a> and <b> must be defined");
  1528.         else
  1529.          Error("panic: neither <a> nor <b> is a wreath product element");
  1530.         fi;
  1531.     fi;
  1532.  
  1533.     # return the power
  1534.     return pow;
  1535. end;
  1536.  
  1537. WreathProductElementOps.Print := function ( a )
  1538.     local   i;
  1539.     Print( "WreathProductElement( " );
  1540.     for i  in [1..Length(a.element)-1]  do
  1541.         Print( a.element[i], ", " );
  1542.     od;
  1543.     i := Length(a.element);
  1544.     Print( a.element[i], ", ", a.permutation, " )" );
  1545. end;
  1546.  
  1547.  
  1548. #############################################################################
  1549. ##
  1550. #F  IsWreathProduct(<D>)  . . . . . . . . test if a group is a wreath product
  1551. ##
  1552. IsWreathProduct := function ( D )
  1553.     return     IsRec( D )
  1554.            and IsBound( D.isWreathProduct )
  1555.            and D.isWreathProduct;
  1556. end;
  1557.  
  1558.  
  1559. #############################################################################
  1560. ##
  1561. #F  WreathProduct(<N>,<G>,<a>) .  . . . . . . . . .  wreath product of groups
  1562. ##
  1563. WreathProduct := function ( arg )
  1564.     local   W,          # wreath product of the arguments, result
  1565.             N,          # factor of the normal subgroup, first argument
  1566.             G,          # factor group, second argument
  1567.             a;          # homomorphisms of <G> into <P>, third argument
  1568.  
  1569.     # get and check the arguments
  1570.     N := arg[1];
  1571.     if not IsGroup( N )  then
  1572.         Error("<N> must be a group");
  1573.     fi;
  1574.     G := arg[2];
  1575.     if not IsGroup( G )  then
  1576.         Error("<G> must be a group");
  1577.     fi;
  1578.     if Length(arg) = 2  then
  1579.         a := OperationHomomorphism( G, Operation(G,Elements(G),OnRight) );
  1580.     else
  1581.         a := arg[3];
  1582.         if     not IsHomomorphism( a )
  1583.             or not IsSubgroup( a.source, G )
  1584.             or not IsPermGroup( a.range )
  1585.         then
  1586.             Error("<a> must be a homomorphisms of <G> into a permgroup");
  1587.         fi;
  1588.     fi;
  1589.  
  1590.     # make the wreath product
  1591.     W := G.operations.WreathProduct( N, G, a );
  1592.  
  1593.     # return the wreath product
  1594.     return W;
  1595. end;
  1596.  
  1597. GroupOps.WreathProduct := function ( N, G, a )
  1598.     local   W,          # wreath product of <grps>, result
  1599.             deg,        # degree of the permutation group
  1600.             id,         # identity of <W>
  1601.             gens,       # generators of <W>
  1602.             gen,        # one generator of <W>
  1603.             grp,        # one group in <grps>
  1604.             i, j;       # loop variables
  1605.  
  1606.     # get the degree
  1607.     deg := PermGroupOps.LargestMovedPoint( Image( a, G ) );
  1608.  
  1609.     # make the identity
  1610.     id := rec( );
  1611.     id.isGroupElement   := true;
  1612.     id.isWreathProductElement := true;
  1613.     id.domain           := GroupElements;
  1614.     id.element          := [];
  1615.     for i  in [ 1 .. deg ]  do
  1616.         id.element[i]   := N.identity;
  1617.     od;
  1618.     id.element[deg+1]   := G.identity;
  1619.     id.permutation      := ();
  1620.     id.operations       := WreathProductElementOps;
  1621.  
  1622.     # make the set of generators
  1623.     gens := [];
  1624.     for i  in [ 1 .. deg ]  do
  1625.         for j  in [ 1 .. Length( N.generators ) ]  do
  1626.             gen := ShallowCopy( id );
  1627.             gen.element := ShallowCopy( id.element );
  1628.             gen.element[i] := N.generators[j];
  1629.             Add( gens, gen );
  1630.         od;
  1631.     od;
  1632.     for j  in [ 1 .. Length( G.generators ) ]  do
  1633.         gen := ShallowCopy( id );
  1634.         gen.element := ShallowCopy( id.element );
  1635.         gen.element[deg+1] := G.generators[j];
  1636.         gen.permutation := G.generators[j] ^ a;
  1637.         Add( gens, gen );
  1638.     od;
  1639.  
  1640.     # make the wreath product
  1641.     W := Group( gens, id );
  1642.  
  1643.     # tag it as wreath product
  1644.     W.isWreathProduct   := true;
  1645.  
  1646.     # enter the information that relates to the construction
  1647.     W.groups            := [ N, G ];
  1648.     W.degree            := deg;
  1649.     W.mapping           := a;
  1650.  
  1651.     # enter the operations record
  1652.     W.operations        := WreathProductOps;
  1653.  
  1654.     # return the wreath product
  1655.     return W;
  1656. end;
  1657.  
  1658.  
  1659. #############################################################################
  1660. ##
  1661. #V  WreathProductOps  . . . . . . operations record for wreath product groups
  1662. ##
  1663. WreathProductOps := Copy( GroupOps );
  1664.  
  1665. WreathProductOps.Size := function ( D )
  1666.     return Size( D.groups[1] ) ^ D.degree * Size( D.groups[2] );
  1667. end;
  1668.  
  1669. WreathProductOps.\in := function ( g, D )
  1670.     return     IsWreathProductElement( g )
  1671.            and IsWreathProduct( D )
  1672.            and Length( g.element ) = Length( D.identity.element )
  1673.            and ForAll( [1..Length(g.element)-1],
  1674.                        i -> g.element[i] in D.groups[1] )
  1675.            and g.element[Length(g.element)] in D.groups[2];
  1676. end;
  1677.  
  1678. #############################################################################
  1679. ##
  1680. #E  Emacs . . . . . . . . . . . . . . . . . . . . . . . local emacs variables
  1681. ##
  1682. ##  Local Variables:
  1683. ##  mode:               outline
  1684. ##  outline-regexp:     "#F\\|#V\\|#E\\|#R"
  1685. ##  fill-column:        73
  1686. ##  fill-prefix:        "##  "
  1687. ##  eval:               (hide-body)
  1688. ##  End:
  1689. ##
  1690.  
  1691.  
  1692.  
  1693.