1#############################################################################
2##
3#W  gp2obj.gi                 GAP4 package `XMod'               Chris Wensley
4#W                                                                & Murat Alp
5#Y  Copyright (C) 2001-2019, Chris Wensley et al,
6#Y  School of Computer Science, Bangor University, U.K.
7##
8##  This file contains generic methods for (pre-)crossed modules and
9##  (pre-)cat1-groups.
10##
11
12##############################################################################
13##
14##  Global constants which determine whether to read cat1data.g
15##  CAT1_LIST_CLASS_SIZES  for groups of size <= CAT1_LIST_MAX_SIZE
16##
17
18CAT1_LIST_MAX_SIZE := 70;
19CAT1_LIST_CLASS_SIZES :=
20    List( [1..CAT1_LIST_MAX_SIZE], n -> NumberSmallGroups(n) );
21CAT1_LIST_LOADED := false;
22CAT1_LIST := [ ];
23
24##############################################################################
25##
26#M  IsPerm2DimensionalGroup . . check whether source and range are perm groups
27#M  IsFp2DimensionalGroup . . . check whether source and range are fp groups
28#M  IsPc2DimensionalGroup . . . check whether source and range are pc groups
29##
30InstallMethod( IsPerm2DimensionalGroup, "generic method for 2d-group objects",
31    true, [ Is2DimensionalGroup ], 0,
32function( obj )
33    return ( IsPermGroup( Source( obj ) ) and IsPermGroup( Range( obj ) ) );
34end );
35
36InstallMethod( IsFp2DimensionalGroup, "generic method for 2d-group objects",
37    true, [ Is2DimensionalGroup ], 0,
38function( obj )
39    return ( IsFpGroup( Source( obj ) ) and IsFpGroup( Range( obj ) ) );
40end );
41
42InstallMethod( IsPc2DimensionalGroup, "generic method for 2d-group objects",
43    true, [ Is2DimensionalGroup ], 0,
44function( obj )
45    return ( IsPcGroup( Source( obj ) ) and IsPcGroup( Range( obj ) ) );
46end );
47
48#############################################################################
49##
50#M  IsPreXMod . . . . . . . . check that the first crossed module axiom holds
51##
52InstallMethod( IsPreXMod, "generic method for 2d-group",
53    true, [ Is2DimensionalGroup ], 0,
54function( P )
55
56    local Xsrc, Xrng, bdy, a, aut, act, gensrc, ngsrc, genrng, ngrng,
57          ssrc, x1, y1, z1, x2, y2, z2, w2;
58
59    if not IsPreXModObj( P ) then
60        return false;
61    fi;
62    Xrng := Range( P );
63    genrng := GeneratorsOfGroup( Xrng );
64    ngrng := Length( genrng );
65    Xsrc := Source( P );
66    gensrc := GeneratorsOfGroup( Xsrc );
67    ngsrc := Length( gensrc );
68    ssrc := Size( Xsrc );
69    bdy := Boundary( P );
70    # Check  P.boundary: P.source -> P.range
71    if not ( ( Source( bdy ) = Xsrc ) and ( Range( bdy ) = Xrng ) ) then
72        Info( InfoXMod, 2,
73              "Error: require  X.boundary : X.source -> X.range" );
74        return false;
75    fi;
76    # checking  IsHomomorphism(bdy) gives cokernel error when Xsrc = [ ]
77    if ( ssrc > 1 ) then
78        if not IsGroupHomomorphism( bdy ) then
79            Info( InfoXMod, 2,
80                  "Error:  the boundary map is NOT a homomorphism!" );
81            return false;
82        fi;
83    fi;
84    act := XModAction( P );
85    aut := Range( act );
86    # Check  X.aut  is a group of automorphisms  X.source -> X.source
87    if not ( IsGroup( aut ) ) then
88        Info( InfoXMod, 2,
89              "Error: group of actions on X.source does not exist!" );
90        return false;
91    fi;
92    if ( aut = Group( IdentityMapping( Xsrc ) ) ) then
93       SetIsTrivialAction2DimensionalGroup( P, true );
94    else
95        a := GeneratorsOfGroup( aut )[1];
96        if not ( ( Source( a ) = Xsrc ) and ( Range( a ) = Xsrc )
97                                        and IsBijective( a )  ) then
98            Info( InfoXMod, 2,
99                  "Require automorphism  X.aut.1  on  X.source" );
100            return false;
101        fi;
102    fi;
103    # Check  X.action: X.range -> X.aut
104    if not ( ( Source( act ) = Xrng ) and ( Range( act ) = aut ) ) then
105        Info( InfoXMod, 2,
106              "Error: require  X.action : X.range -> X.aut" );
107        return false;
108    fi;
109    if ( Size( aut ) = 1 ) then
110        Info( InfoXMod, 2,
111              "X.action trivial => not checking a homomorphism!" );
112    else
113        if not IsGroupHomomorphism( act ) then
114            Info( InfoXMod, 2, " X.action is not a homomorphism|" );
115            return false;
116        fi;
117    fi;
118    Info( InfoXMod, 3,
119          "Checking  CM1) bdy(x2^x1) = (bdy(x2))^x1 " );
120    for x1 in genrng do
121        for x2 in gensrc do
122            # Print( "x1,x2 = ", x1, ",  ", x2, "\n" );
123            y1 := ( x2 ^ ( x1^act ) ) ^ bdy;
124            z1 := ( x2 ^ bdy ) ^ x1;
125            if ( y1 <> z1 ) then
126                Info( InfoXMod, 3,
127                    "CM1) fails at  x1 = ", x1, ",  x2 = ", x2, "\n",
128                    "  bdy(x2^x1) = ", y1, "\n", "(bdy(x2))^x1 = ", z1 );
129                return false;
130            fi;
131        od;
132    od;
133    return true;
134end );
135
136##############################################################################
137##
138#M  \=( <P>, <Q> )  . . . . . . . .  test if two pre-crossed modules are equal
139##
140InstallMethod( \=, "generic method for two pre-crossed modules",
141    IsIdenticalObj, [ IsPreXMod, IsPreXMod ], 0,
142function ( P, Q )
143    return ( ( Boundary(P) = Boundary(Q) )
144         and ( XModAction(P) = XModAction(Q) ) );
145end );
146
147##############################################################################
148##
149#M  Size( <P> )  . . . . . . . . . . . . . . . . size for a pre-crossed module
150##
151InstallOtherMethod( Size, "generic method for a 2d-object",
152    [ Is2DimensionalDomain ], 20,
153function ( obj )
154    return [ Size( Source( obj ) ), Size( Range( obj ) ) ];
155end );
156
157#############################################################################
158##
159#M  IsTrivialAction2DimensionalGroup  . . . . check whether action is trivial
160##
161InstallMethod( IsTrivialAction2DimensionalGroup,
162    "generic method for pre-crossed modules", true, [ IsPreXMod ], 0,
163function( PM )
164
165    local act, genrng, onesrc;
166
167    act := XModAction( PM );
168    genrng := GeneratorsOfGroup( Range( PM ) );
169    onesrc := IdentityMapping( Source( PM ) );
170    return ForAll( genrng, r -> ( ImageElm( act, r ) = onesrc ) );
171end );
172
173###############################################################################
174##
175#M  IsCentralExtension2DimensionalGroup( <xmod> ) . . . . . . check the axioms
176##
177InstallMethod( IsCentralExtension2DimensionalGroup, "for an xmod",
178    true, [ IsXMod ], 0,
179function( X0 )
180
181    local S, R, bdy, act, genS, genR, preR, len, i, r, pr, actr, s;
182
183    S := Source( X0 );
184    R := Range( X0 );
185    bdy := Boundary( X0 );
186    if not IsSurjective( bdy ) then
187        return false;
188    fi;
189    if not IsSubgroup( Centre( S ), Kernel( bdy ) ) then
190        return false;
191    fi;
192    act := XModAction( X0 );
193    genS := GeneratorsOfGroup( S );
194    genR := GeneratorsOfGroup( R );
195    preR := List( genR, r -> PreImagesRepresentative( bdy, r ) );
196    len := Length( genR );
197    for i in [1..len] do
198        r := genR[i];
199        pr := preR[i];
200        actr := ImageElm( act, r );
201        for s in genS do
202            if not ( ImageElm( actr, s ) = s^pr ) then
203                return false;
204            fi;
205        od;
206    od;
207    return true;
208end );
209
210##############################################################################
211##
212#M  PreXModObj( <bdy>, <act> ) . . . . . . . . . . . make a pre-crossed module
213##
214InstallMethod( PreXModObj, "for homomorphism and action", true,
215    [ IsGroupHomomorphism, IsGroupHomomorphism ], 0,
216function( bdy, act )
217
218    local type, PM, ok, src, rng, aut, name;
219
220    src := Source( bdy );
221    rng := Range( bdy );
222    if not ( rng = Source( act ) ) then
223        Error( "require Range( bdy ) = Source( act )" );
224    fi;
225    aut := Range( act );
226    if not IsGroupOfAutomorphisms( aut ) then
227        Error( "Range( act ) must be a group of automorphisms" );
228    fi;
229    if ( IsPermGroup( src ) and IsPermGroup( rng ) ) then
230        type := PermPreXModObjType;
231    elif ( IsPcGroup( src ) and IsPcGroup( rng ) ) then
232        type := PcPreXModObjType;
233    else
234        type := PreXModObjType;
235    fi;
236    PM := rec();
237    ObjectifyWithAttributes( PM, type,
238      Source, src,
239      Range, rng,
240      Boundary, bdy,
241      XModAction, act,
242      IsPreXModDomain, true,
243      Is2DimensionalGroup, true );
244    if not IsPreXMod( PM ) then
245        Info( InfoXMod, 1, "Warning: not a pre-crossed module." );
246    else
247        ok := IsXMod( PM ); # for running properly the function AllXMods
248    fi;
249    # name := Name( PM );
250    ## check the types
251    if ( IsPermGroup(src) and IsPermGroup(rng) ) then
252        SetIsPerm2DimensionalGroup( PM, true );
253    elif ( IsPcGroup(src) and IsPcGroup(rng) ) then
254        SetIsPc2DimensionalGroup( PM, true );
255    fi;
256    return PM;
257end );
258
259#############################################################################
260##
261#M  ImageElmXModAction( <pxmod>, <s>, <r> )  pre-xmod module action of s on r
262##
263InstallMethod( ImageElmXModAction, "method for a precrossed module", true,
264    [ Is2DimensionalDomain, IsObject, IsObject ], 0,
265function( PM, s, r )
266
267    local actr;
268
269    if ( HasIsPreXModWithObjects(PM) and IsPreXModWithObjects(PM) ) then
270        ## this is the crossed module of groupoids case
271        actr := ImageElm( XModAction( PM ), r )![1];
272        return ImageElm( actr, s );
273    else
274        return ImageElm( ImageElm( XModAction(PM), r ), s );
275    fi;
276end );
277
278#############################################################################
279##
280#M  ExternalSetXMod( <pxm> ) . . . . . . . source group as a range group set
281##
282InstallMethod( ExternalSetXMod, "method for a precrossed module", true,
283    [ IsPreXMod ], 0,
284function( PM )
285
286    local rng, genR, act;
287
288    rng := Range( PM  );
289    genR := GeneratorsOfGroup( rng );
290    act := XModAction( PM );
291    return ExternalSet( rng, Source(PM), genR,
292               List( genR, g -> ImageElm( act, g ) ) );
293end );
294
295#############################################################################
296##
297#M  String, ViewString, PrintString, ViewObj, PrintObj
298##  . . . . . . . . . . . . . . . . . . . . . . . for two-dimensional domains
299##
300InstallMethod( String, "for a 2d-domain", true, [ Is2DimensionalDomain ], 0,
301function( g2d )
302    return( STRINGIFY( "[", String( Source(g2d) ), " -> ",
303                            String( Range(g2d) ), "]" ) );
304end );
305
306InstallMethod( ViewString, "for a 2d-domain", true, [ Is2DimensionalDomain ],
307    0, String );
308
309InstallMethod( PrintString, "for a 2d-domain", true, [ Is2DimensionalDomain ],
310    0, String );
311
312InstallMethod( ViewObj, "for a 2d-domain", true, [ Is2DimensionalDomain ],
313    0,
314function( g2d )
315    if HasName( g2d ) then
316        Print( Name( g2d ), "\n" );
317    elif ( HasIsPreXModDomain( g2d ) and IsPreXModDomain( g2d ) ) then
318        Print( "[", Source( g2d ), "->", Range( g2d ), "]" );
319    elif ( HasIsPreCat1Domain( g2d ) and IsPreCat1Domain( g2d ) ) then
320        Print( "[", Source( g2d ), "=>", Range( g2d ), "]" );
321    else
322        TryNextMethod();
323    fi;
324end );
325
326InstallMethod( PrintObj, "for a 2d-domain", true, [ Is2DimensionalDomain ], 0,
327function( g2d )
328    if HasName( g2d ) then
329        Print( Name( g2d ), "\n" );
330    elif ( HasIsPreXModDomain( g2d ) and IsPreXModDomain( g2d ) ) then
331        Print( "[", Source( g2d ) );
332        if IsGroupoid( Source( g2d ) ) then
333            Print( "\n->  " );
334        else
335            Print( " -> " );
336        fi;
337        Print( Range( g2d ), "]" );
338    elif ( HasIsPreCat1Domain( g2d ) and IsPreCat1Domain( g2d ) ) then
339        Print( "[", Source( g2d ) );
340        if IsGroupoid( Source( g2d ) ) then
341            Print( "\n=>  " );
342        else
343            Print( " => " );
344        fi;
345        Print( Range( g2d ), "]" );
346    else
347        TryNextMethod();
348    fi;
349end );
350
351#############################################################################
352##
353#F  Display( <g2d> ) . . . . . . . . . . . . . . print details of a 2d-group
354##
355InstallMethod( Display, "method for prexmods and precat2groups", true,
356    [ Is2DimensionalGroup ], 20,
357function( g2d )
358
359    local name, bdy, act, aut, len, i, ispar, src, rng,
360           gensrc, genrng, ker, genker, mor, triv, imact, a,
361           t, h, e, b, k, imt, imh, ime, imb, imk;
362
363    src := Source( g2d );
364    rng := Range( g2d );
365    if ( HasName(src) and HasName(rng) ) then
366        name := Name( g2d );
367    else
368        name := "[??->??]";
369    fi;
370    gensrc := GeneratorsOfGroup( src );
371    genrng := GeneratorsOfGroup( rng );
372    if ( HasIsPreXMod( g2d ) and IsPreXMod( g2d ) ) then
373        if ( HasIsXMod( g2d ) and IsXMod( g2d ) ) then
374            Print( "\nCrossed module " );
375        else
376            Print( "\nPre-crossed module " );
377        fi;
378    elif ( HasIsPreCat1Group( g2d ) and IsPreCat1Group( g2d ) ) then
379        if ( HasIsCat1Group( g2d ) and IsCat1Group( g2d ) ) then
380            Print( "\nCat1-group " );
381        else
382            Print( "\nPre-cat1-group " );
383        fi;
384    else
385        Print( "WARNING: neither a PreXMod nor a PreCat1Group" );
386    fi;
387    if HasName( g2d ) then
388        Print( Name(g2d), " :- \n" );
389    else
390        Print( ":- \n" );
391    fi;
392    ispar := not HasParent( src );
393    if ( ispar and HasName( src ) ) then
394        Print( ": Source group ", src );
395    elif ( ispar and HasName( Parent( src ) ) ) then
396        Print( ": Source group has parent ( ", Parent( src), " ) and" );
397    else
398        Print( ": Source group" );
399    fi;
400    Print( " has generators:\n" );
401    Print( "  ", gensrc, "\n" );
402    ispar := not HasParent( rng );
403    if ( ispar and HasName( rng ) ) then
404        Print( ": Range group ", rng );
405    elif ( ispar and HasName( Parent( rng ) ) ) then
406        Print( ": Range group has parent ( ", Parent( rng ), " ) and" );
407    else
408        Print( ": Range group" );
409    fi;
410    Print( " has generators:\n" );
411    Print( "  ", genrng, "\n" );
412    if ( HasIsPreXMod( g2d ) and IsPreXMod( g2d ) ) then
413        Print( ": Boundary homomorphism maps source generators to:\n" );
414        bdy := Boundary( g2d );
415        Print( "  ",  List( gensrc, s -> ImageElm( bdy, s ) ), "\n" );
416        act := XModAction( g2d );
417        imact := List( genrng, r -> ImageElm( act, r ) );
418        aut := Range( act );
419        triv := ( aut = Group( InclusionMappingGroups( src, src ) ) );
420        len := Length( genrng );
421        if ( len = 0 ) then
422            triv := true;
423        else
424            for i in [1..len] do
425                a := imact[i];
426            od;
427        fi;
428        if not triv then
429            Print( ": Action homomorphism maps" );
430            Print( " range generators to automorphisms:\n" );
431            for i in [1..len] do
432                Print( "  ", genrng[i], " --> { source gens --> " );
433                Print( List( gensrc, s -> ImageElm( imact[i], s ) ), " }\n" );
434            od;
435        fi;
436        if triv then
437            Print( "  The automorphism group is trivial\n" );
438        else
439            if ( len = 1 ) then
440                Print( "  This automorphism generates" );
441            else
442                Print( "  These ", len, " automorphisms generate" );
443            fi;
444            Print( " the group of automorphisms.\n" );
445        fi;
446    else  ## g2d is at least a PreCat1Group
447        ker := Kernel( g2d );
448        genker := GeneratorsOfGroup( ker );
449        t := TailMap( g2d );
450        h := HeadMap( g2d );
451        e := RangeEmbedding( g2d );
452        b := Boundary( g2d );
453        k := KernelEmbedding( g2d );
454        imt := List( gensrc, x -> ImageElm( t, x ) );
455        imh := List( gensrc, x -> ImageElm( h, x ) );
456        ime := List( genrng, x -> ImageElm( e, x ) );
457        imb := List( genker, x -> ImageElm( b, x ) );
458        imk := List( genker, x -> ImageElm( k, x ) );
459        Print( ": tail homomorphism maps source generators to:\n" );
460        Print( "  ", imt, "\n" );
461        Print( ": head homomorphism maps source generators to:\n" );
462        Print( "  ", imh, "\n" );
463        Print( ": range embedding maps range generators to:\n" );
464        Print( "  ", ime, "\n" );
465        if ( Size( ker ) = 1 ) then
466            Print( ": the kernel is trivial.\n" );
467        else
468            Print( ": kernel has generators:\n" );
469            Print( "  ", genker, "\n" );
470            Print( ": boundary homomorphism maps generators of kernel to:\n" );
471            Print( "  ", imb, "\n" );
472            Print( ": kernel embedding maps generators of kernel to:\n" );
473            Print( "  ", imk, "\n" );
474        fi;
475    fi;
476    if ( HasIsXMod( g2d ) and IsXMod( g2d ) and HasCat1GroupOfXMod( g2d ) ) then
477        Print( ": associated cat1-group is ",
478               Cat1GroupOfXMod( g2d ), "\n" );
479    elif ( HasIsCat1Group( g2d ) and IsCat1Group( g2d )
480           and HasXModOfCat1Group( g2d ) ) then
481        Print( ": associated crossed module is ",
482               XModOfCat1Group( g2d ), "\n" );
483    fi;
484    Print( "\n" );
485end );
486
487#############################################################################
488##
489#M  IdGroup . . . . . . . . . . . . . . . . . . . . for a 2Dimensional-domain
490#M  StructureDescription  . . . . . . . . . . . . . for a 2Dimensional-domain
491##
492InstallOtherMethod( IdGroup, "method for a 2d-domain", true,
493    [ Is2DimensionalDomain ], 0,
494function( dom )
495    return [ IdGroup( Source(dom) ), IdGroup( Range(dom) ) ];
496end );
497
498InstallOtherMethod( StructureDescription, "method for a 2d-domain", true,
499    [ Is2DimensionalDomain ], 0,
500function( dom )
501    return [ StructureDescription( Source(dom) ),
502             StructureDescription( Range(dom) ) ];
503end );
504
505#############################################################################
506##
507#M  Name . . . . . . . . . . . . . . . . . . . . .  for a 2Dimensional-domain
508##
509InstallMethod( Name, "method for a 2d-domain", true,
510    [ Is2DimensionalDomain ], 0,
511function( dom )
512
513    local nsrc, nrng, name, arrow;
514
515    if HasName( Source( dom ) ) then
516        nsrc := Name( Source( dom ) );
517    else
518        nsrc := "..";
519    fi;
520    if HasName( Range( dom ) ) then
521        nrng := Name( Range( dom ) );
522    else
523        nrng := "..";
524    fi;
525    if ( HasIsPreXModDomain( dom ) and IsPreXModDomain( dom ) ) then
526        arrow := "->";
527    elif ( HasIsPreCat1Domain( dom ) and IsPreCat1Domain( dom ) ) then
528        arrow := "=>";
529    else
530        arrow := "->-";
531    fi;
532    name := Concatenation( "[", nsrc, arrow, nrng, "]" );
533    SetName( dom, name );
534    return name;
535end );
536
537#############################################################################
538##
539#M  PreXModByBoundaryAndAction
540##
541InstallMethod( PreXModByBoundaryAndAction,
542    "pre-crossed module from boundary and action maps",
543    true, [ IsGroupHomomorphism, IsGroupHomomorphism ], 0,
544function( bdy, act )
545
546    local rng, src, genrng, gensrc, aut, genaut, imact, i, a0, ima, a, PX;
547
548    src := Source( bdy );
549    gensrc := GeneratorsOfGroup( src );
550    rng := Range( bdy );
551    genrng := GeneratorsOfGroup( rng );
552    if not ( Source( act ) = rng ) then
553        Info( InfoXMod, 2,
554              "The range group is not the source of the action." );
555        return fail;
556    fi;
557    aut := Range( act );
558    genaut := GeneratorsOfGroup( aut );
559    if not IsGroupOfAutomorphisms( aut ) then
560        Info( InfoXMod, 2, "<aut> is not a group of automorphisms" );
561        return fail;
562    fi;
563    for a in genaut do
564        if not ( ( Source( a ) = src ) and ( Range( a ) = src ) ) then
565            Info( InfoXMod, 2, "error in source and range of automorphism" );
566            return fail;
567        fi;
568    od;
569    if not ( One( aut ) = IdentityMapping( src ) ) then
570        Info( InfoXMod, 2,
571              "aut.identity <> IdentityMapping( src )" );
572        return fail;
573    fi;
574    imact := List( genrng, r -> ImageElm( act, r ) );
575    for i in [ 1..Length( imact ) ] do
576        a0 := imact[i];
577        ima := List( gensrc, s -> ImageElm( a0, s ) );
578        a := GroupHomomorphismByImages( src, src, gensrc, ima );
579        imact[i] := a;
580    od;
581    PX := PreXModObj( bdy, act );
582    if not IsPreXMod( PX ) then
583        Error( "PX fails to be a pre-crossed module" );
584    fi;
585    return PX;
586end );
587
588#############################################################################
589##
590#M  IsPreCat1Group            check that the first pre-cat1-group axiom holds
591##
592InstallMethod( IsPreCat1Group, "generic method for 2dim-group", true,
593    [ Is2DimensionalGroup ], 0,
594function( C1G )
595
596    local Csrc, Crng, x, e, t, h, idrng, he, te, kert, kerh, kerth;
597
598    if not IsPreCat1Obj( C1G ) then
599        return false;
600    fi;
601    Crng := Range( C1G );
602    h := HeadMap( C1G );
603    t := TailMap( C1G );
604    e := RangeEmbedding( C1G );
605    # checking the first condition of cat-1 group
606    idrng := IdentityMapping( Crng );
607    he := CompositionMapping( h, e );
608    te := CompositionMapping( t, e );
609    if not ( te = idrng ) then
610        Print( "te <> range identity \n" );
611        return false;
612    fi;
613    if not ( he = idrng ) then
614        Print( "he <> range identity \n" );
615        return false;
616    fi;
617    return true;
618end );
619
620##############################################################################
621##
622#M  \=( <C1>, <C2> ) . . . . . . . . . . test if two pre-cat1-groups are equal
623##
624InstallMethod( \=, "generic method for pre-cat1-groups",
625    IsIdenticalObj, [ IsPreCat1Group, IsPreCat1Group ], 0,
626function( C1, C2 )
627    return ( ( TailMap(C1) = TailMap(C2) ) and ( HeadMap(C1) = HeadMap(C2) )
628             and ( RangeEmbedding(C1) = RangeEmbedding(C2) ) );
629end );
630
631##############################################################################
632##
633#M  PreCat1Obj . . . . . . . . . . . . . . . . . . . . . make a pre-cat1-group
634##
635InstallMethod( PreCat1Obj, "for tail, head, embedding", true,
636    [ IsGroupHomomorphism, IsGroupHomomorphism, IsGroupHomomorphism ], 0,
637function( t, h, e )
638
639    local src, rng, type, C1G, ok, name;
640
641    src := Source( t );
642    rng := Range( t );
643    if not ( ( src = Source( h ) ) and ( rng = Range( h ) ) ) then
644        Error( "tail & head must have same source and range" );
645    fi;
646    if not ( ( Source( e ) = rng ) and ( Range( e ) = src ) ) then
647        Error( "tail, embedding must have opposite source and range" );
648    fi;
649    if ( IsPermGroup( src ) and IsPermGroup( rng ) ) then
650        type := PermPreCat1ObjType;
651    elif ( IsPcGroup( src ) and IsPcGroup( rng ) ) then
652        type := PcPreCat1ObjType;
653    else
654        type := PreCat1ObjType;
655    fi;
656    C1G := rec();
657    ObjectifyWithAttributes( C1G, type,
658      Source, src,
659      Range, rng,
660      TailMap, t,
661      HeadMap, h,
662      RangeEmbedding, e,
663      IsPreCat1Domain, true,
664      Is2DimensionalGroup, true );
665    ok := IsPreCat1Group( C1G );
666    if not ok then
667        Error( "not a pre-cat1-group" );
668    fi;
669    ok := IsPreCat1GroupByEndomorphisms( C1G );
670    ok := IsCat1Group( C1G );
671    ## check the types
672    if ( IsPermGroup( src ) and IsPermGroup( rng ) ) then
673        SetIsPerm2DimensionalGroup( C1G, true );
674    elif ( IsPcGroup( src ) and IsPcGroup( rng ) ) then
675        SetIsPc2DimensionalGroup( C1G, true );
676    fi;
677    if ( HasName( src ) and HasName( rng ) ) then
678        name := Name( C1G );
679    fi;
680    return C1G;
681end );
682
683##############################################################################
684##
685#M  Elements( <P> )  . . . . . . . . . . . . elements for a pre-crossed module
686##
687##  replaced by Enumerator ???
688
689#############################################################################
690##
691#M  ReverseCat1Group                                     for a pre-cat1-group
692##
693InstallMethod( ReverseCat1Group, "method for a cat1-group", true,
694    [ IsPreCat1Group ], 0,
695function( C1G )
696    local rev;
697    rev := PreCat1Group( HeadMap(C1G), TailMap(C1G), RangeEmbedding(C1G ) );
698    SetReverseCat1Group( rev, C1G );
699    return rev;
700end );
701
702#############################################################################
703##
704#F  PreCat1Group( <t>, <h>, <e> ) pre-cat1-group from given tail, head, embed
705#F  PreCat1Group( <t>, <h> )     pre-cat1-group from tail, head endomorphisms
706##
707InstallGlobalFunction( PreCat1Group, function( arg )
708
709    local nargs, usage, C1G;
710
711    nargs := Length( arg );
712    usage := "standard usage: PreCat1Group( tail, head [,embed] );";
713    if not ForAll( arg, a -> IsGroupHomomorphism(a) ) then
714        Error( usage );
715    fi;
716    # one endomorphism
717    if ( ( nargs=1 ) and IsEndoMapping( arg[1] ) ) then
718        return PreCat1GroupByEndomorphisms( arg[1], arg[1] );
719    # two endomorphisms
720    elif ( nargs=2 ) then
721        if ( IsEndoMapping( arg[1] ) and IsEndoMapping( arg[2] ) ) then
722            return PreCat1GroupByEndomorphisms( arg[1], arg[2] );
723        elif ( Image( arg[1] ) = Source( arg[2] ) ) then
724            return PreCat1GroupByTailHeadEmbedding( arg[1], arg[1], arg[2] );
725        fi;
726    # two homomorphisms and an embedding
727    elif ( nargs=3 ) then
728        return PreCat1GroupByTailHeadEmbedding( arg[1], arg[2], arg[3] );
729    fi;
730    # other alternatives not allowed
731    Error( usage );
732end );
733
734##############################################################################
735##
736#M  PreCat1GroupOfPreXMod . . convert a pre-crossed module to a pre-cat1-group
737##
738InstallMethod( PreCat1GroupOfPreXMod,
739    "convert a pre-crossed module to a pre-cat1-group", true, [ IsPreXMod ], 0,
740function( X0 )
741
742    local S0, genS0, R0, genR0, iso, Xact, Xbdy, one, imbdy, info, G, genG,
743          t, h, f, eR, eS, imeR, imeS, projS, imt, imh, ime, imf, C, pcrec;
744
745    S0 := Source( X0 );
746    genS0 := GeneratorsOfGroup( S0 );
747    R0 := Range( X0 );
748    genR0 := GeneratorsOfGroup( R0 );
749    genS0 := GeneratorsOfGroup( S0 );
750    genR0 := GeneratorsOfGroup( R0 );
751    one := One( R0 );
752    Xact := XModAction( X0 );
753    Xbdy := Boundary( X0 );
754    if IsTrivialAction2DimensionalGroup( X0 ) then
755        Info( InfoXMod, 2, "Using direct product: ", R0, " x ", S0 );
756        G := DirectProduct( R0, S0 );
757        info := DirectProductInfo( G );
758        if ( HasName( S0 ) and HasName( R0 ) ) then
759            SetName( G, Concatenation( Name( R0 ), "x", Name( S0 ) ) );
760        fi;
761        genG := GeneratorsOfGroup( G );
762        imbdy := List( genS0, s -> ImageElm( Xbdy, s ) );
763        imt := Concatenation( genR0, List( genS0, s -> one ) );
764        imh := Concatenation( genR0, imbdy );
765        t := GroupHomomorphismByImages( G, R0, genG, imt );
766        h := GroupHomomorphismByImages( G, R0, genG, imh );
767        eR := Embedding( G, 1 );
768        eR := AsGroupGeneralMappingByImages( eR );
769        eS := Embedding( G, 2 );
770        eS := AsGroupGeneralMappingByImages( eS );
771    else
772        Info( InfoXMod, 2, "Using semidirect product: ", R0, " |X ", S0 );
773        G := SemidirectProduct( R0, Xact, S0 );
774        info := SemidirectProductInfo( G );
775        if ( HasName( S0 ) and HasName( R0 ) ) then
776             SetName( G,
777                 Concatenation( "(", Name(R0), " |X ", Name(S0), ")" ) );
778        else
779             SetName( G, "(..|X..)" );
780        fi;
781        genG := GeneratorsOfGroup( G );
782        eR := Embedding( G, 1 );
783        imeR := List( genR0, r -> ImageElm( eR, r ) );
784        eS := Embedding( G, 2 );
785        imeS := List( genS0, s -> ImageElm( eS, s ) );
786        t := Projection( G );
787        imt := List( genG, g -> ImageElm( t, g ) );
788        t := GroupHomomorphismByImages( G, R0, genG, imt );
789        projS := List( imt, r -> ImageElm( eR, r^-1 ) );
790        projS := List( [ 1..Length( genG ) ], i -> projS[i] * genG[i] );
791        projS := List( projS, x -> PreImagesRepresentative( eS, x ) );
792        imh := List( [ 1..Length( genG ) ],
793            i -> imt[i] * ImageElm( Xbdy, projS[i] ) );
794        h := GroupHomomorphismByImages( G, R0, genG, imh );
795    fi;
796    C := PreCat1GroupByTailHeadEmbedding( t, h, eR );
797    if HasName( X0 ) then
798        SetName( C, Concatenation( "cat1(", Name( X0 ), ")" ) );
799    fi;
800    pcrec := rec( precat1 := C,
801                  xmodRangeEmbedding := Image( eR ),
802                  xmodRangeEmbeddingIsomorphism := eR,
803                  xmodSourceEmbedding := Image( eS ),
804                  xmodSourceEmbeddingIsomorphism := eS );
805    if HasIsXMod( X0 ) and IsXMod( X0 ) then
806        pcrec.iscat1 := true;
807    fi;
808    return pcrec;
809end );
810
811#############################################################################
812##
813#M  IsXMod . . . . . . . . . check that the second crossed module axiom holds
814##
815InstallMethod( IsXMod, "generic method for pre-crossed modules",
816    true, [ IsPreXMod ], 0,
817function( XM )
818
819    local gensrc, genrng, x2, y2, w2, z2, hom, act;
820
821    hom := Boundary( XM );
822    act := XModAction( XM );
823    gensrc := GeneratorsOfGroup( Source( XM ) );
824    genrng := GeneratorsOfGroup( Range( XM ) );
825    for x2 in gensrc do
826        for y2 in gensrc do
827            # Print( "x2,y2 = ", x2, ",  ", y2, "\n" );
828            z2 := x2 ^ ((y2 ^ hom) ^ act);
829            w2 := x2 ^ y2;
830            if ( z2 <> w2 ) then
831                Info( InfoXMod, 2,
832                      "CM2) fails at  x2 = ", x2, ",  y2 = ", y2, "\n",
833                      "x2^(hom(y2)) = ", z2, "\n","      x2^y2 = ", w2, "\n" );
834                return false;
835            fi;
836        od;
837    od;
838    return true;
839end );
840
841#############################################################################
842##
843#M  XModByBoundaryAndAction
844##
845InstallMethod( XModByBoundaryAndAction,
846    "crossed module from boundary and action maps", true,
847    [ IsGroupHomomorphism, IsGroupHomomorphism ], 0,
848function( bdy, act )
849
850    local PM;
851
852    PM := PreXModByBoundaryAndAction( bdy, act );
853    if not IsXMod( PM ) then
854        Error( "this boundary and action only defines a pre-crossed module" );
855    fi;
856    return PM;
857end );
858
859#############################################################################
860##
861#M  XModByTrivialAction
862##
863InstallMethod( XModByTrivialAction, "crossed module with trivial action",
864    true, [ IsGroupHomomorphism ], 0,
865function( f )
866    local R, ZR, S, XM, aut, act, name;
867    S := Source( f );
868    if not IsAbelian( S ) then
869        Error( "the source of  f  must be abelian" );
870    fi;
871    R := Range( f );
872    ZR := Centre( R );
873    if not IsSubgroup( ZR, Image( f, S ) ) then
874        Error( "image of source must lie in the centre of range" );
875    fi;
876    aut := Group( IdentityMapping( S ) );
877    act := MappingToOne( R, aut );
878    XM := XModByBoundaryAndAction( f, act );
879    SetIsTrivialAction2DimensionalGroup( XM, true );
880    return XM;
881end );
882
883##############################################################################
884##
885#F  XModByNormalSubgroup            create a crossed module from normal N in G
886##
887InstallMethod( XModByNormalSubgroup, "conjugation crossed module",
888    true, [ IsGroup, IsGroup ], 0,
889function( G, N )
890
891    local XM, bdy, act, aut, genrng, gensrc, name, a, triv, idsrc,
892           autgen, imautgen, phi, j, g, n, genN, f2pN, imgenN;
893
894    if not IsNormal( G, N ) then
895        return fail;
896    fi;
897    genrng := GeneratorsOfGroup( G );
898    gensrc := GeneratorsOfGroup( N );
899    bdy := GroupHomomorphismByImages( N, G, gensrc, gensrc );
900    autgen := [ ];
901    for g in genrng do
902        imautgen := List( gensrc, n -> n^g );
903        a := GroupHomomorphismByImages( N, N, gensrc, imautgen );
904        Add( autgen, a );
905    od;
906    if ( Length( genrng ) = 0 ) then
907        idsrc := IdentityMapping( N );
908        aut := Group( idsrc );
909        Info( InfoXMod, 2,
910              "Group of conjugations has size ", Size( aut ) );
911    else
912        aut := Group( autgen );
913    fi;
914    SetIsGroupOfAutomorphisms( aut, true );
915    act := GroupHomomorphismByImages( G, aut, genrng, autgen );
916    XM := PreXModObj( bdy, act );
917    SetIsNormalSubgroup2DimensionalGroup( XM, true );
918    if ( Length( autgen ) = 0 ) then
919        SetIsTrivialAction2DimensionalGroup( XM, true );
920    fi;
921    return XM;
922end );
923
924#############################################################################
925##
926#F  XModByCentralExtension           xmod from surjection with central kernel
927##
928InstallMethod( XModByCentralExtension, "central extension crossed module",
929    true, [ IsGroupHomomorphism ], 0,
930function( hom )
931
932    local rng, src, Zsrc, ker, gensrc, ngsrc, imhom, genrng, autgen,
933           j, imsrc, aut, act, XM, ok, idsrc;
934
935    if not IsSurjective( hom ) then
936        Error( "homomorphism must be surjective" );
937    fi;
938    src := Source( hom );
939    rng := Range( hom );
940    Zsrc := Centre( src );
941    ker := Kernel( hom );
942    if not IsSubgroup( Zsrc, ker ) then
943        Error( "Kernel of surjection is not central" );
944    fi;
945    gensrc := GeneratorsOfGroup( src );
946    ngsrc := Length( gensrc );
947    imhom := List( gensrc, s -> ImageElm( hom, s ) );
948    genrng := GeneratorsOfGroup( rng );
949    autgen := ListWithIdenticalEntries( ngsrc, 0 );
950    for j in [1..ngsrc] do
951        imsrc := List( gensrc, s -> s^gensrc[j] );
952        autgen[j] := GroupHomomorphismByImages( src, src, gensrc, imsrc );
953    od;
954    aut := Group( autgen );
955    SetIsGroupOfAutomorphisms( aut, true );
956    Info( InfoXMod, 2, "Group of conjugations has size ", Size(aut) );
957    act := GroupHomomorphismByImages( rng, aut, imhom, autgen );
958    if ( not IsGroupHomomorphism( act ) ) then
959        Error( "action is not a homomorphism" );
960    fi;
961    XM := PreXModObj( hom, act );
962    SetIsCentralExtension2DimensionalGroup( XM, true );
963    idsrc := IdentityMapping( src );
964    if ForAll( autgen, a -> ( a = idsrc ) ) then
965        SetIsTrivialAction2DimensionalGroup( XM, true );
966    fi;
967    ok := IsXMod( XM );
968    return XM;
969end );
970
971#############################################################################
972##
973#M  XModByAbelianModule( <abmod> )      crossed module  [zero : abmod -> grp]
974##
975InstallMethod( XModByAbelianModule, "abelian module crossed module", true,
976    [ IsAbelianModule ], 0,
977function( abmod )
978
979    local aut, act, z;
980    act := AbelianModuleAction( abmod );
981    z := MappingToOne( AbelianModuleGroup( abmod ), Source( act ) );
982    return XModByBoundaryAndAction( z, act );
983end );
984
985#############################################################################
986##
987#M  XModByGroupOfAutomorphisms                       crossed module  [G -> A]
988##
989InstallMethod( XModByGroupOfAutomorphisms, "automorphism crossed module",
990    true, [ IsGroup, IsGroup ], 0,
991function( G, A )
992
993    local genA, autG, innG, abelian, genG, oneA, imbdy, g, ima, a, bdy, act,
994          iso1, A1, bdy1, act1, iso2, iso12, bdy2, act2, XM;
995
996    if not IsGroupOfAutomorphisms( A ) then
997        Error( "A is not a group of automorphisms" );
998    fi;
999    genA := GeneratorsOfGroup( A );
1000    autG := AutomorphismGroup( G );
1001    if not IsSubgroup( autG, A ) then
1002        Error( "A is not a group of automorphisms of G" );
1003    fi;
1004    innG := InnerAutomorphismsAutomorphismGroup( autG );
1005    if not IsSubgroup( A, innG ) then
1006        Error( "the inner automorphism group of G is not a subgroup of A" );
1007    fi;
1008    abelian := IsAbelian( G );
1009    genG := GeneratorsOfGroup( G );
1010    oneA := One( A );
1011    if abelian then
1012        imbdy := List( genG, g -> oneA );
1013    else
1014        imbdy := [ ];
1015        for g in genG do
1016            ima := List( genG, h -> h^g );
1017            a := GroupHomomorphismByImages( G, G, genG, ima );
1018            Add( imbdy, a );
1019        od;
1020    fi;
1021    bdy := GroupHomomorphismByImages( G, A, genG, imbdy );
1022    XM := PreXModObj( bdy, IdentityMapping( A ) );
1023    SetIsAutomorphismGroup2DimensionalGroup( XM, true );
1024    if not IsXMod( XM ) then
1025        Error( "this boundary and action only defines a pre-crossed module" );
1026    fi;
1027    return XM;
1028end );
1029
1030#############################################################################
1031##
1032#M  XModByAutomorphismGroup( <G> )               crossed module [G -> Aut(G)]
1033#M  XModByInnerAutomorphismGroup( <G> )          crossed module [G -> Inn(G)]
1034##
1035InstallMethod( XModByAutomorphismGroup, "automorphism xmod of a group",
1036    true, [ IsGroup ], 0,
1037function( G )
1038
1039    local  autG, innG, a;
1040
1041    autG := AutomorphismGroup( G );
1042    if ( not HasName( autG ) and HasName( G ) ) then
1043        SetName( autG, Concatenation( "Aut(", Name( G ), ")" ) );
1044    fi;
1045    SetIsGroupOfAutomorphisms( autG, true );
1046    return XModByGroupOfAutomorphisms( G, autG );
1047end );
1048
1049InstallMethod( XModByInnerAutomorphismGroup, "inner automorphism xmod",
1050    true, [ IsGroup ], 0,
1051function( G )
1052    local A, innG, a;
1053
1054    A := InnerAutomorphismsByNormalSubgroup( G, G );
1055    if ( not HasName( A ) and HasName( G ) ) then
1056        SetName( A, Concatenation( "Inn(", Name( G ), ")" ) );
1057    fi;
1058    SetIsGroupOfAutomorphisms( A, true );
1059    return XModByGroupOfAutomorphisms( G, A );
1060end );
1061
1062##############################################################################
1063##
1064#M  XModOfCat1Group
1065##
1066InstallMethod( XModOfCat1Group, "generic method for cat1-groups",
1067    true, [ IsCat1Group ], 0,
1068function( C1 )
1069
1070    local X1;
1071    X1 := PreXModOfPreCat1Group( C1 );
1072    SetIsXMod( X1, true );
1073    SetXModOfCat1Group( C1, X1 );
1074    SetCat1GroupOfXMod( X1, C1 );
1075    return X1;
1076end );
1077
1078##############################################################################
1079##
1080#M  Cat1GroupOfXMod
1081##
1082InstallMethod( Cat1GroupOfXMod, "generic method for crossed modules",
1083    true, [ IsXMod ], 0,
1084function( X1 )
1085
1086    local PC1, C1;
1087
1088    PC1 := PreCat1GroupOfPreXMod( X1 );
1089    C1 := PC1.precat1;
1090    SetXModOfCat1Group( C1, X1 );
1091    return C1;
1092end );
1093
1094##############################################################################
1095##
1096#M  PeifferSubgroupPreXMod . . . . . normally generated by Peiffer commutators
1097##
1098InstallMethod( PeifferSubgroupPreXMod, "generic method for pre-crossed xmods",
1099    true, [ IsPreXMod ], 0,
1100function( PM )
1101
1102    local Pf, s1, s2, a1, src, gensrc, comm, bdy, act, ok, XPf;
1103
1104    # this code mimics that of DerivedSubgroup
1105    src := Source( PM );
1106    bdy := Boundary( PM );
1107    act := XModAction( PM );
1108    gensrc := GeneratorsOfGroup( src );
1109    Pf := TrivialSubgroup( src );
1110    for s1 in gensrc do
1111        a1 := ImageElm( act, ImageElm( bdy, s1 ) );
1112        for s2 in gensrc do
1113            comm := (s2^-1)^s1 * ImageElm( a1, s2 );
1114            if not ( comm in Pf ) then
1115                Pf := ClosureSubgroup( Pf, comm );
1116            fi;
1117        od;
1118    od;
1119    Pf := NormalClosure( src, Pf );
1120    if ( Pf = src ) then
1121        Pf := src;
1122    fi;
1123    XPf := Sub2DimensionalGroup( PM, Pf, TrivialSubgroup( Range(PM) ) );
1124    ok := IsNormal( PM, XPf );
1125    SetPeifferSub2DimensionalGroup( PM, XPf );
1126    return Pf;
1127end );
1128
1129##############################################################################
1130##
1131#M  PeifferSubgroupPreCat1Group . . . . commutator of kernels of tail and head
1132##
1133InstallMethod( PeifferSubgroupPreCat1Group,
1134    "generic method for pre-cat1-groups", true, [ IsPreCat1Group ], 0,
1135function( PCG )
1136
1137    local src, kerh, kert, Pf;
1138
1139    src := Source( PCG );
1140    kert := Kernel( TailMap( PCG ) );
1141    kerh := Kernel( HeadMap( PCG ) );
1142    Pf := CommutatorSubgroup( kert, kerh );
1143    if ( Pf = src ) then
1144        Pf := src;
1145    fi;
1146    return Pf;
1147end );
1148
1149##############################################################################
1150##
1151#M  PeifferSubgroup . . . . . . . .
1152##
1153InstallMethod( PeifferSubgroup, "generic method for 2d-groups",
1154               true, [ Is2DimensionalGroup ], 0,
1155function( obj )
1156    local P, ok, NP;
1157    if IsPreXModObj( obj ) then
1158        if IsXMod( obj ) then
1159            return Subgroup( Source( obj ), [ One( Source( obj ) ) ] );
1160        else
1161            P := PeifferSubgroupPreXMod( obj );
1162            NP := SubPreXMod( obj, P, TrivialSubgroup( Range(obj) ) );
1163            ok := IsNormal( obj, NP );
1164            return P;
1165        fi;
1166    elif IsPreCat1Obj( obj ) then
1167        if IsCat1Group( obj ) then
1168            return Subgroup( Source( obj ), [ One( Source( obj ) ) ] );
1169        else
1170            P := PeifferSubgroupPreCat1Group( obj );
1171            NP := SubPreCat1Group( obj, P, TrivialSubgroup( Range(obj) ) );
1172            ok := IsNormal( obj, NP );
1173            return P;
1174        fi;
1175    else
1176        return fail;
1177    fi;
1178end );
1179
1180##############################################################################
1181##
1182#A  XModByPeifferQuotient . . . . . . . xmod from prexmod and Peiffer subgroup
1183##
1184InstallMethod( XModByPeifferQuotient,
1185    "crossed module from a pre-crossed module and Peiffer subgroup", true,
1186    [ IsPreXMod ], 0,
1187function( PM )
1188
1189    local pfsub, pfxmod, name, nat, ok, FM;
1190
1191    if IsXMod( PM ) then
1192        Info( InfoXMod, 1, "this object is already a crossed module!" );
1193        return PM;
1194    fi;
1195    pfsub := PeifferSubgroup( PM );
1196    if not IsNormal( Source( PM ), pfsub ) then
1197        Error( "Peiffer subgroup not normal in source group" );
1198    fi;
1199    pfxmod := SubPreXMod( PM, pfsub, TrivialSubgroup( Range(PM) ) );
1200    ok := IsNormal( PM, pfxmod );
1201    if not ok then
1202        Error( "Peiffer precrossed module not normal!" );
1203    fi;
1204    FM := FactorPreXMod( PM, pfxmod );
1205    nat := ProjectionOfFactorPreXMod( FM );
1206    if HasName( PM ) then
1207        name := Name( PM );
1208        SetName( FM, Concatenation( "Peiffer(", name, ")" ) );
1209    fi;
1210    return FM;
1211end );
1212
1213##############################################################################
1214##
1215#O  XModByPullback . . . . . . . . xmod from an xmod and a group homomorphism
1216##
1217InstallMethod( XModByPullback,
1218    "crossed module from a crossed module and a group homomorphism", true,
1219    [ IsXMod, IsGroupHomomorphism ], 0,
1220function( X0, nu )
1221
1222    local M, P, act0, N, L, genL, lenL, info, dp, emb1, emb2, lambda,
1223          kappa, genLN, genLM, autL, genN, lenN, imact1, i, n, an,
1224          actLN, actLM, prod, act1, X1, mor;
1225
1226    M := Source( X0 );
1227    P := Range( X0 );
1228    if not ( Range( nu ) = P ) then
1229        Error( "Range(hom) <> Range(X0)" );
1230    fi;
1231    act0 := XModAction( X0 );
1232    N := Source( nu );
1233    L := Pullback( Boundary( X0 ), nu );
1234    genL := GeneratorsOfGroup( L );
1235    lenL := Length( genL );
1236    info := PullbackInfo( L );
1237    dp := info!.directProduct;
1238    emb1 := Embedding( dp, 1 );
1239    emb2 := Embedding( dp, 2 );
1240    kappa := info!.projections[1];
1241    lambda := info!.projections[2];
1242    genLN := List( genL, g -> ImageElm( lambda, g ) );
1243    genLM := List( genL, g -> ImageElm( kappa, g ) );
1244    autL := AutomorphismGroup( L );
1245    genN := GeneratorsOfGroup( N );
1246    lenN := Length( genN );
1247    imact1 := ListWithIdenticalEntries( lenN, 0 );
1248    for i in [1..lenN] do
1249        n := genN[i];
1250        an := ImageElm( act0, ImageElm( nu, n ) );
1251        actLN := List( genLN, g -> g^n );
1252        actLM := List( genLM, g -> ImageElm( an, g ) );
1253        prod := List( [1..lenL], j -> ImageElm( emb1, actLM[j] )
1254                                    * ImageElm( emb2, actLN[j] ) );
1255        imact1[i] := GroupHomomorphismByImages( L, L, genL, prod );
1256    od;
1257    act1 := GroupHomomorphismByImages( N, autL, genN, imact1 );
1258    X1 := XMod( lambda, act1 );
1259    mor := XModMorphismByGroupHomomorphisms( X1, X0, kappa, nu );
1260    SetMorphismOfPullback( X1, mor );
1261    return X1;
1262end );
1263
1264##############################################################################
1265##
1266#A  KernelCokernelXMod . . . . . ( ker(bdy) -> range/image(bdy) ) for an xmod
1267##
1268InstallMethod( KernelCokernelXMod, "kernel -> cokernel for an xmod", true,
1269    [ IsXMod ], 0,
1270function( X0 )
1271
1272    local S, R, bdy, act, K, J, nat, F, iso, mgi, inv, C, genK, imres, res,
1273          genC, preC, imact, i, g, p, ap, im, autK, actC;
1274
1275    S := Source( X0 );
1276    R := Range( X0 );
1277    bdy := Boundary( X0 );
1278    act := XModAction( X0 );
1279    K := Kernel( bdy );
1280    J := Image( bdy );
1281    if ( J = R ) then ## trivial cokernel
1282        C := Group( () );
1283        res := MappingToOne( K, C );
1284        return XModByTrivialAction( res );
1285    fi;
1286    nat := NaturalHomomorphismByNormalSubgroup( R, J );
1287    F := FactorGroup( R, J );
1288    iso := IsomorphismPermGroup( F );
1289    C := Image( iso );
1290    mgi := MappingGeneratorsImages( iso );
1291    inv := GroupHomomorphismByImages( C, F, mgi[2], mgi[1] );
1292    genK := GeneratorsOfGroup( K );
1293    imres := List( genK, g -> Image( iso, Image( nat, Image( bdy, g ) ) ) );
1294    res := GroupHomomorphismByImages( K, C, genK, imres );
1295    genC := GeneratorsOfGroup( C );
1296    preC := List( genC,
1297                  g -> PreImagesRepresentative( nat, ImageElm( inv, g ) ) );
1298    imact := ShallowCopy( genC );
1299    for i in [1..Length( genC )] do
1300        g := genC[i];
1301        p := preC[i];
1302        ap := ImageElm( act, p );
1303        im := List( genK, k -> ImageElm( ap, k ) );
1304        imact[i] := GroupHomomorphismByImages( K, K, genK, im );
1305    od;
1306    autK := Group( imact );
1307    SetIsGroupOfAutomorphisms( autK, true );
1308    actC := GroupHomomorphismByImages( C, autK, genC, imact );
1309    return XModByBoundaryAndAction( res, actC );
1310end );
1311
1312#############################################################################
1313##
1314#F  XMod( <bdy>, <act> )          crossed module from given boundary & action
1315#F  XMod( <G>, <N> )              crossed module from a normal inclusion
1316#F  XMod( <surj> )                crossed module from a surjective hom
1317#F  XMod( <cat1> )                crossed module associated to a cat1-group
1318#F  XMod( <aut> )                 crossed module from automorphism group
1319#F  XMod( <pxm> )                 crossed module by Peiffer quotient
1320##
1321InstallGlobalFunction( XMod, function( arg )
1322
1323    local nargs;
1324    nargs := Length( arg );
1325
1326    # two homomorphisms
1327    if ( ( nargs = 2 ) and IsGroupHomomorphism( arg[1] )
1328                       and IsGroupHomomorphism( arg[2] ) ) then
1329        return XModByBoundaryAndAction( arg[1], arg[2] );
1330
1331    # group and normal subgroup
1332    elif ( ( nargs = 2 ) and IsGroup( arg[1] ) and IsGroup( arg[2] )
1333      and IsSubgroup( arg[1], arg[2] ) and IsNormal( arg[1], arg[2] ) ) then
1334        return XModByNormalSubgroup( arg[1], arg[2] );
1335
1336    # xmod plus list of objects plus boolean
1337    elif ( ( nargs = 3 ) and IsXMod( arg[1] )
1338           and IsList( arg[2] ) and IsBool( arg[3] ) ) then
1339        return SinglePiecePreXModWithObjects( arg[1], arg[2], arg[3] );
1340
1341    # surjective homomorphism
1342    elif ( ( nargs = 1 ) and IsGroupHomomorphism( arg[1] )
1343                         and IsSurjective( arg[1] ) ) then
1344        return XModByCentralExtension( arg[1] );
1345
1346    # convert a cat1-group
1347    elif ( ( nargs = 1 ) and HasIsCat1Group( arg[1] )
1348           and IsCat1Group( arg[1] ) ) then
1349        return PreXModOfPreCat1Group( arg[1] );
1350
1351    # group of automorphisms
1352    elif ( ( nargs = 1 ) and IsGroupOfAutomorphisms( arg[1] ) ) then
1353        return XModByAutomorphismGroup( arg[1] );
1354
1355    # just a group
1356    elif ( ( nargs = 1 ) and IsGroup( arg[1] ) ) then
1357        return XModByNormalSubgroup( arg[1], arg[1] );
1358
1359    # pre-crossed module
1360    elif ( ( nargs = 1 ) and IsPreXMod( arg[1] ) ) then
1361        return XModByPeifferQuotient( arg[1] );
1362
1363    fi;
1364    # alternatives not allowed
1365    Error( "usage: XMod( bdy, act );  or  XMod( G, N );" );
1366end );
1367
1368##############################################################################
1369##
1370#M  IsSubPreXMod
1371##
1372InstallMethod( IsSubPreXMod, "generic method for pre-crossed modules", true,
1373    [ Is2DimensionalGroup, Is2DimensionalGroup ], 0,
1374function( PM, SM )
1375
1376    local ok, Ssrc, Srng, gensrc, genrng, s, r, r1, r2, im1, im2;
1377
1378    if not ( IsPreXMod( PM ) and IsPreXMod( SM ) ) then
1379        return false;
1380    fi;
1381    if ( HasParent( SM ) and ( Parent( SM ) = PM ) ) then
1382        return true;
1383    fi;
1384    Ssrc := Source( SM );
1385    Srng := Range( SM );
1386    if not (     IsSubgroup( Source( PM ), Ssrc )
1387             and IsSubgroup( Range( PM ), Srng ) ) then
1388        Info( InfoXMod, 3, "IsSubgroup failure in IsSubPreXMod" );
1389        return false;
1390    fi;
1391    ok := true;
1392    gensrc := GeneratorsOfGroup( Ssrc );
1393    genrng := GeneratorsOfGroup( Srng );
1394    for s in gensrc do
1395        if ( ImageElm( Boundary(PM), s ) <> ImageElm( Boundary(SM), s ) ) then
1396            ok := false;
1397        fi;
1398    od;
1399    if not ok then
1400        Info( InfoXMod, 3, "boundary maps have different images" );
1401        return false;
1402    fi;
1403    for r in genrng do
1404        r1 := ImageElm( XModAction( PM ), r );
1405        r2 := ImageElm( XModAction( SM ), r );
1406        for s in gensrc do
1407            im1 := ImageElm( r1, s );
1408            im2 := ImageElm( r2, s );
1409            if ( im1 <> im2 ) then
1410                ok := false;
1411                Info( InfoXMod, 3, "s,im1,im2 = ", [s,im1,im2] );
1412            fi;
1413        od;
1414    od;
1415    if not ok then
1416        Info( InfoXMod, 3, "actions have different images" );
1417        return false;
1418    fi;
1419    if ( PM <> SM ) then
1420        SetParent( SM, PM );
1421    fi;
1422    return true;
1423end );
1424
1425##############################################################################
1426##
1427#M  IsSubXMod( <XM>, <SM> )
1428##
1429InstallMethod( IsSubXMod, "generic method for crossed modules", true,
1430    [ Is2DimensionalGroup, Is2DimensionalGroup ], 0,
1431function( XM, SM )
1432
1433    if not ( IsXMod( XM ) and IsXMod( SM ) ) then
1434        return false;
1435    fi;
1436    return IsSubPreXMod( XM, SM );
1437end );
1438
1439##############################################################################
1440##
1441#M  IsSubPreCat1Group
1442##
1443InstallMethod( IsSubPreCat1Group, "generic method for pre-cat1-groups", true,
1444    [ Is2DimensionalGroup, Is2DimensionalGroup ], 0,
1445function( C0, S0 )
1446
1447    local ok, Ssrc, Srng, gensrc, genrng, tc, hc, ec, ts, hs, es, s, r;
1448
1449    if not ( IsPreCat1Group( C0 ) and IsPreCat1Group( S0 ) ) then
1450        return false;
1451    fi;
1452    if ( HasParent( S0 ) and ( Parent( S0 ) = C0 ) ) then
1453        return true;
1454    fi;
1455    Ssrc := Source( S0 );
1456    Srng := Range( S0 );
1457    if not (     IsSubgroup( Source( C0 ), Ssrc )
1458             and IsSubgroup( Range( C0 ), Srng ) ) then
1459        Info( InfoXMod, 3, "IsSubgroup failure in IsSubPreCat1Group" );
1460        return false;
1461    fi;
1462    ok := true;
1463    gensrc := GeneratorsOfGroup( Ssrc );
1464    genrng := GeneratorsOfGroup( Srng );
1465    tc := TailMap(C0);  hc := HeadMap(C0);  ec := RangeEmbedding(C0);
1466    ts := TailMap(S0);  hs := HeadMap(S0);  es := RangeEmbedding(S0);
1467    for s in gensrc do
1468        if ( ImageElm( tc, s ) <> ImageElm( ts, s ) ) then
1469            ok := false;
1470        fi;
1471        if ( ImageElm( hc, s ) <> ImageElm( hs, s ) ) then
1472            ok := false;
1473        fi;
1474    od;
1475    if not ok then
1476        Info( InfoXMod, 3, "tail/head maps have different images" );
1477        return false;
1478    fi;
1479    for r in genrng do
1480        if ( ImageElm( ec, r ) <> ImageElm( es, r ) ) then
1481            ok := false;
1482        fi;
1483    od;
1484    if not ok then
1485        Info( InfoXMod, 3, "embeddingss have different images" );
1486        return false;
1487    fi;
1488    if ( C0 <> S0 ) then
1489        SetParent( S0, C0 );
1490    fi;
1491    return true;
1492end );
1493
1494##############################################################################
1495##
1496#M  IsSubCat1Group( <C1>, <S1> )
1497##
1498InstallMethod( IsSubCat1Group, "generic method for cat1-groups", true,
1499    [ Is2DimensionalGroup, Is2DimensionalGroup ], 0,
1500function( C1, S1 )
1501
1502    if not ( IsCat1Group( C1 ) and IsCat1Group( S1 ) ) then
1503        return false;
1504    fi;
1505    return IsSubPreCat1Group( C1, S1 );
1506end );
1507
1508##############################################################################
1509##
1510#M  IsNormalSub2DimensionalDomain( <XM>, <SM> )
1511##
1512InstallMethod( IsNormalSub2DimensionalDomain, "for xmod and subxmod etc.",
1513    true, [ Is2DimensionalGroup, Is2DimensionalGroup ], 0,
1514function( X0, X1 )
1515
1516    local ispx, ok, S0, R0, S1, R1, r0, s0, r1, s1;
1517
1518    ispx := IsPreXMod( X0 );
1519    if ispx then
1520        ok := IsSubPreXMod( X0, X1 );
1521    else
1522        ok := IsSubPreCat1Group( X0, X1 );
1523    fi;
1524    if not ok then
1525        return false;
1526    fi;
1527    if not ispx then
1528        Error( "not yet installed for cat1-groups" );
1529    fi;
1530    S0 := Source( X0 );
1531    R0 := Range( X0 );
1532    S1 := Source( X1 );
1533    R1 := Range( X1 );
1534    if not IsNormal( R0, R1 ) then
1535        return false;
1536    fi;
1537    ## apparently no requirement for S1 to be normal in S0
1538    for r0 in GeneratorsOfGroup( R0 ) do
1539        for s1 in GeneratorsOfGroup( S1 ) do
1540            if not ImageElmXModAction( X0, s1, r0 ) in S1 then
1541                return false;
1542            fi;
1543        od;
1544    od;
1545    for r1 in GeneratorsOfGroup( R1 ) do
1546        for s0 in GeneratorsOfGroup( S0 ) do
1547            if not s0^-1 * ImageElmXModAction( X0, s0, r1 ) in S1 then
1548                return false;
1549            fi;
1550        od;
1551    od;
1552    return true;
1553end );
1554
1555##############################################################################
1556##
1557#M  Sub2DimensionalGroup . .  creates Sub2bObject from Ssrc<=Osrc & Srng<=Orng
1558##
1559InstallMethod( Sub2DimensionalGroup, "generic method for 2d-objects", true,
1560    [ Is2DimensionalGroup, IsGroup, IsGroup ], 0,
1561function( obj, src, rng )
1562    if ( HasIsXMod(obj) and IsXMod(obj) ) then
1563        return SubXMod( obj, src, rng );
1564    elif ( HasIsPreXMod(obj) and IsPreXMod(obj) ) then
1565        return SubPreXMod( obj, src, rng );
1566    elif ( HasIsCat1Group(obj) and IsCat1Group(obj) ) then
1567        return SubCat1Group( obj, src, rng );
1568    elif ( HasIsPreCat1Group(obj) and IsPreCat1Group(obj) ) then
1569        return SubPreCat1Group( obj, src, rng );
1570    else
1571        Error( "unknown type of 2d-object" );
1572    fi;
1573end );
1574
1575##############################################################################
1576##
1577#M  SubPreXMod                 creates SubPreXMod from Ssrc<=Psrc & Srng<=Prng
1578##
1579InstallMethod( SubPreXMod, "generic method for pre-crossed modules", true,
1580    [ IsPreXMod, IsGroup, IsGroup ], 0,
1581function( PM, Ssrc, Srng )
1582
1583    local Psrc, Prng, Pbdy, Pact, Paut, genSsrc, genSrng, Pname, Sname,
1584           SM, Sbdy, Saut, Sact, r, innaut, genPrng, genPsrc, ssrc,
1585           trivsrc, trivrng, incSsrc, idSsrc, imact, imgen, imbdy, imSsrc,
1586           imalpha, alpha;
1587
1588    Psrc := Source( PM );
1589    Prng := Range( PM );
1590    Pbdy := Boundary( PM );
1591    Pact := XModAction( PM );
1592    Paut := Range( Pact );
1593    if not IsSubgroup( Psrc, Ssrc ) then
1594        Print( "Ssrc is not a subgroup of Psrc\n" );
1595        return fail;
1596    fi;
1597    if not ( IsSubgroup( Prng, Srng ) ) then
1598        Print( "Srng is not a subgroup of Prng\n" );
1599        return fail;
1600    fi;
1601    ssrc := Size( Ssrc );
1602    genPsrc := GeneratorsOfGroup( Psrc );
1603    genPrng := GeneratorsOfGroup( Prng );
1604    genSsrc := GeneratorsOfGroup( Ssrc );
1605    genSrng := GeneratorsOfGroup( Srng );
1606    incSsrc := InclusionMappingGroups( Psrc, Ssrc );
1607    imgen := List( genSsrc, x -> ImageElm( Pbdy, x ) );
1608    imSsrc := Subgroup( Prng, imgen );
1609    if not IsSubgroup( Srng, imSsrc ) then
1610        Info( InfoXMod, 2, "Pbdy(Ssrc) is not a subgroup of Srng" );
1611        return fail;
1612    fi;
1613    trivsrc := ( Size( Ssrc ) = 1 );
1614    trivrng := ( Size( Srng ) = 1 );
1615    if ( trivrng or trivsrc ) then
1616        Sbdy := MappingToOne( Ssrc, Srng );
1617    else
1618        Sbdy:= GroupHomomorphismByImages( Ssrc, Srng, genSsrc, imgen );
1619    fi;
1620    innaut := [ ];
1621    for r in genSrng do
1622        alpha := ImageElm( Pact, r );
1623        imgen := List( genSsrc, x -> ImageElm( alpha, x ) );
1624        if not ForAll( imgen, x -> ( x in Ssrc ) ) then
1625            return fail;
1626        fi;
1627        imalpha := Subgroup( Ssrc, imgen );
1628        if not ( IsSubgroup( Ssrc, imalpha ) ) then
1629            Info( InfoXMod, 2, "Srng does not act correctly on Ssrc" );
1630            return fail;
1631        fi;
1632        alpha:=GroupHomomorphismByImages( Ssrc, Ssrc, genSsrc, imgen );
1633        Add( innaut, alpha );
1634    od;
1635    idSsrc := IdentityMapping( Ssrc );
1636    if ( ssrc = 1 ) then
1637        Saut := Group( idSsrc );
1638        innaut := List( genSrng, s -> idSsrc );
1639    else
1640        Saut := Group( innaut, idSsrc );
1641    fi;
1642    Sact := GroupHomomorphismByImages( Srng, Saut, genSrng, innaut );
1643    if ( not IsGroupHomomorphism( Sact ) ) then
1644        Print( "Sact is not a homomorphism\n" );
1645        return fail;
1646    fi;
1647    SM := PreXModByBoundaryAndAction( Sbdy, Sact );
1648    if HasParent( PM ) then
1649        SetParent( SM, Parent( PM ) );
1650    else
1651        SetParent( SM, PM );
1652    fi;
1653    return SM;
1654end );
1655
1656##############################################################################
1657##
1658#M  SubXMod . . . . . . . . . . . creates SubXMod from Ssrc<=Psrc & Srng<=Prng
1659##
1660InstallMethod( SubXMod, "generic method for crossed modules", true,
1661    [ IsXMod, IsGroup, IsGroup ], 0,
1662function( XM, Ssrc, Srng )
1663
1664    local SM;
1665    SM := SubPreXMod( XM, Ssrc, Srng );
1666    if ( SM = fail ) then
1667        return fail;
1668    fi;
1669    if not IsXMod( SM ) then
1670        Error( "the result is only a pre-crossed module" );
1671    fi;
1672    return SM;
1673end );
1674
1675###############################################################################
1676##
1677#M  SubPreCat1Group . .  created from PreCat1Group and a subgroup of the source
1678##
1679InstallMethod( SubPreCat1Group, "generic method for (pre-)cat1-groups", true,
1680    [ IsPreCat1Group, IsGroup, IsGroup ], 0,
1681function( C, G, R )
1682
1683    local Csrc, Crng, Ct, Ch, Ce, t, h, e, SC, ok;
1684
1685    Csrc := Source( C );
1686    Crng := Range( C );
1687    Ct := TailMap( C );
1688    Ch := HeadMap( C );
1689    Ce := RangeEmbedding( C );
1690    ok := true;
1691    if not ( IsSubgroup( Csrc, G ) ) then
1692        Print( "G is not a subgroup of Csrc\n" );
1693        ok := false;
1694    fi;
1695    if not ( ( R = Image( Ct, G ) ) and
1696             ( R = Image( Ch, G ) ) ) then
1697        Print( "restrictions of Ct, Ch to G must have common image R\n" );
1698        ok := false;
1699    fi;
1700    t := GeneralRestrictedMapping( Ct, G, R );
1701    h := GeneralRestrictedMapping( Ch, G, R );
1702    e := GeneralRestrictedMapping( Ce, R, G );
1703    SC := PreCat1GroupByTailHeadEmbedding( t, h, e );
1704    if not ( C = SC ) then
1705        SetParent( SC, C );
1706    fi;
1707    return SC;
1708end );
1709
1710##############################################################################
1711##
1712#M  SubCat1Group . . creates SubCat1Group from Cat1Group and subgroup of source
1713##
1714InstallMethod( SubCat1Group, "generic method for cat1-groups", true,
1715    [ IsCat1Group, IsGroup, IsGroup ], 0,
1716function( C, G, R )
1717
1718    local S;
1719    S := SubPreCat1Group( C, G, R );
1720    if not IsCat1Group( S ) then
1721        Error( "result is only a pre-cat1-group" );
1722    fi;
1723    return S;
1724end );
1725
1726#############################################################################
1727##
1728#M  IsCat1Group                  check that the second cat1-group axiom holds
1729##
1730InstallMethod( IsCat1Group, "generic method for crossed modules", true,
1731    [ IsPreCat1Group ], 0,
1732function( C1G )
1733
1734    local Csrc, Crng, h, t, e, f, kerC, kert, kerh, kerth;
1735
1736    Csrc := Source( C1G );
1737    Crng := Range( C1G );
1738    h := HeadMap( C1G );
1739    t := TailMap( C1G );
1740    e := RangeEmbedding( C1G );
1741    kerC := Kernel( C1G );
1742    f := KernelEmbedding( C1G );
1743    kert := Kernel( t );
1744    kerh := Kernel( h );
1745    kerth := CommutatorSubgroup( kert, kerh );
1746    if not ( Size( kerth ) = 1 ) then
1747        Info( InfoXMod, 1, "condition  [kert,kerh] = 1  is not satisfied");
1748        return false;
1749    fi;
1750    if not ( ( Source( f ) = kerC ) and ( Range( f ) = Csrc ) ) then
1751        Print( "Warning: KernelEmbedding( C1G ) incorrectly defined?\n" );
1752    fi;
1753    return true;
1754end );
1755
1756#############################################################################
1757##
1758#M  IsIdentityPreCat1Group
1759#M  IsPreCat1GroupByEndomorphisms
1760##
1761InstallMethod( IsIdentityPreCat1Group, "test a pre-cat1-group", true,
1762    [ IsPreCat1Group ], 0,
1763function( C1G )
1764    return ( ( TailMap( C1G ) = IdentityMapping( Source( C1G ) ) ) and
1765             ( HeadMap( C1G ) = IdentityMapping( Source( C1G ) ) ) );
1766end );
1767
1768InstallMethod( IsPreCat1GroupByEndomorphisms, "test a pre-cat1-group", true,
1769    [ IsPreCat1Group ], 0,
1770function( obj )
1771    return IsSubgroup( Source(obj), Range(obj) );
1772end );
1773
1774#############################################################################
1775##
1776#F  Cat1Group( <size>, <gpnum>, <num> )     cat1-group from data in CAT1_LIST
1777#F  Cat1Group( <t>, <h>, <e> )              cat1-group from given t,h,e
1778#F  Cat1Group( <t>, <h> )                   cat1-group from t,h endomorphisms
1779##
1780InstallGlobalFunction( Cat1Group, function( arg )
1781
1782    local nargs, C1G, ok;
1783
1784    nargs := Length( arg );
1785    if ( ( nargs < 1 ) or ( nargs > 3 ) ) then
1786        Print( "standard usage: Cat1Group( tail, head [,embed] );\n" );
1787        Print( "            or: Cat1Group( size, gpnum, num );\n" );
1788        return fail;
1789    elif not IsInt( arg[1] ) then
1790        if ( nargs = 1 ) then
1791            C1G := PreCat1Group( arg[1] );
1792        elif ( nargs = 2 ) then
1793            C1G := PreCat1Group( arg[1], arg[2] );
1794        elif ( nargs = 3 ) then
1795            C1G := PreCat1Group( arg[1], arg[2], arg[3] );
1796        fi;
1797        ok := IsCat1Group( C1G );
1798        if ok then
1799            return C1G;
1800        else
1801            Error( "quotient by Peiffer group is not yet implemented" );
1802            return fail;
1803        fi;
1804    else   ## arg[1] is an integer, so use the data in cat1data.g
1805        return Cat1Select( arg[1], arg[2], arg[3] );
1806    fi;
1807end );
1808
1809#############################################################################
1810##
1811#F  Cat1Select( <size>, <gpnum>, <num> )     cat1-group from data in CAT1_LIST
1812##
1813InstallMethod( Cat1Select, "construct a cat1-group using data in file",
1814    true, [ IsInt ], 0,
1815function( size )
1816    return Cat1Select( size, 0, 0 );
1817end );
1818
1819InstallMethod( Cat1Select, "construct a cat1-group using data in file",
1820    true, [ IsInt, IsInt ], 0,
1821function( size, gpnum )
1822    return Cat1Select( size, gpnum, 0 );
1823end );
1824
1825InstallMethod( Cat1Select, "construct a cat1-group using data in file", true,
1826    [ IsInt, IsInt, IsInt ], 0,
1827function( size, gpnum, num )
1828
1829    local ok, type, norm, usage, usage2, maxsize, start, iso, count, comm,
1830           pos, pos2, names, i, j, k, ncat1, G, genG, fam, M, L, genR, R,
1831           imt, t, kert, imh, h, C1G, XC, i0;
1832
1833    maxsize := CAT1_LIST_MAX_SIZE;
1834    usage := "Usage:  Cat1Select( size, gpnum, num );";
1835    usage2 := "   where size <= CAT1_LIST_MAX_SIZE = ";
1836    if not ( ( size > 0 ) and ( size <= maxsize ) ) then
1837        Print( usage, usage2, CAT1_LIST_MAX_SIZE, "\n" );
1838        return fail;
1839    fi;
1840    if ( size = 1 ) then
1841        if ( num = 0 ) then
1842            Print( usage, "\nThere is only " );
1843            Print( "the trivial cat1-structure on the trivial group.\n" );
1844            Print( "(1)  [ [ ],  tail = head = zero mapping ]\n" );
1845            return 1;
1846        elif ( num = 1 ) then
1847            G := SmallGroup( 1, 1 );
1848            t := IdentityMapping( G );
1849            return PreCat1GroupByEndomorphisms( t, t );
1850        else
1851            return fail;
1852        fi;
1853    fi;
1854    if ( CAT1_LIST_LOADED = false ) then
1855        ReadPackage( "xmod", "lib/cat1data.g" );
1856    fi;
1857    # find starting positions of iso classes of groups of size <= maxsize
1858    iso := CAT1_LIST_CLASS_SIZES;
1859    count := 1;
1860    start := [ 1 ];
1861    for j in iso do
1862        count := count + j;
1863        Add( start, count );
1864    od;
1865    Info( InfoXMod, 2, "  iso = ", iso, "\n  start = ", start );
1866    if ( ( size < 1 ) or ( size > maxsize ) ) then
1867        Error( "only groups of order up to ", maxsize, " in CAT1_LIST");
1868        return false;
1869    fi;
1870    pos := start[ size ];
1871    if ( size < maxsize ) then
1872        pos2 := start[ size + 1 ] - 1;
1873    else
1874        pos2 := Length( CAT1_LIST );
1875    fi;
1876    names := List( [ pos..pos2], n -> CAT1_LIST[n][3] );
1877    if not ( gpnum > 0 ) then
1878        Print( usage, "\n" );
1879        return names;
1880    fi;
1881    if ( gpnum > iso[size] ) then
1882        Print( "# isomorphism classes of groups of size ", size,
1883               " is ", iso[size], ", less than ", gpnum, "\n" );
1884        Print( usage, "\n" );
1885        return names;
1886    fi;
1887    j := pos + gpnum - 1;
1888    M := CAT1_LIST[j];
1889    if not ( ( M[1] = size ) and ( M[2] = gpnum ) ) then
1890        Error( "M[1..2] <> [ size, gpnum ]" );
1891    fi;
1892    G  := SmallGroup( size, gpnum );
1893    SetName( G, M[3] );
1894    comm := IsCommutative( G );
1895    if comm then
1896        ncat1 := Length( M[5] ) + 2;
1897        k := 2;
1898    else
1899        ncat1 := Length( M[5] ) + 1;
1900        k := 1;
1901    fi;
1902    if IsPermGroup( G ) then
1903        return PermCat1Select( size, gpnum, num );
1904    fi;
1905    fam := FamilyObj( GeneratorsOfGroup( G )[1] );
1906    genG := List( M[4], e -> ObjByExtRep( fam, e ) );
1907    if not ( ( num >= 1 ) and ( num <= ncat1 ) ) then
1908        Print( usage, "\n" );
1909        Print( "There are ", ncat1, " cat1-structures for the group ");
1910        Print( G, ".\n" );
1911        Print( "Using small generating set ", genG, " for source of homs.\n" );
1912        Print( "[ [range gens]," );
1913        Print( " [tail genimages], [head genimages] ]" );
1914        Print( " :-\n" );
1915        if comm then
1916            Print("(1)  [ ","[ ]",",  tail = head = zero mapping ]\n");
1917        fi;
1918        for i in [1..ncat1-k] do
1919            if comm then i0 := i+1; else i0 := i; fi;
1920            L := M[5][i];
1921            genR := List( L[1], e -> ObjByExtRep( fam, e ) );
1922            imt := List( L[2], e -> ObjByExtRep( fam, e ) );
1923            imh := List( L[3], e -> ObjByExtRep( fam, e ) );
1924            Print( "(", i0, ")  ", [ genR, imt, imh ], "\n" );
1925        od;
1926        Print("(",ncat1,")  [ ",genG,",  tail = head = identity mapping ]\n");
1927        return ncat1;
1928    fi;
1929    if ( num = ncat1 ) then
1930        t := IdentityMapping( G );
1931        h := ShallowCopy( t );
1932    else
1933        if ( ( num = 1 ) and IsCommutative( G ) ) then
1934            R := TrivialSubgroup( G );
1935            SetName( R, "triv" );
1936            t := MappingToOne( G, R );
1937            h := ShallowCopy( t );
1938        else
1939            L := M[5][num-k+1];
1940            genR := List( L[1], e -> ObjByExtRep( fam, e ) );
1941            R := Subgroup( G, genR );
1942            imt := List( L[2], e -> ObjByExtRep( fam, e ) );
1943            t := GroupHomomorphismByImages( G, R, genG, imt );
1944            imh := List( L[3], e -> ObjByExtRep( fam, e ) );
1945            h := GroupHomomorphismByImages( G, R, genG, imh );
1946        fi;
1947        SetIsEndoMapping( t, true );
1948        SetIsEndoMapping( h, true );
1949        kert := Kernel( t );
1950    fi;
1951    C1G := PreCat1GroupByEndomorphisms( t, h );
1952    ok := IsCat1Group( C1G );
1953    if ok then
1954        XC := XModOfCat1Group( C1G );
1955    fi;
1956    return C1G;
1957end );
1958
1959InstallMethod( PermCat1Select, "construct a cat1-group using data in file",
1960    true, [ IsInt, IsInt, IsInt ], 0,
1961function( size, gpnum, num )
1962
1963    local ok, type, norm, maxsize, start, iso, count, pos, pos2, names,
1964           i, j, ncat1, G, genG, fam, M, L, genR, R, t, kert, h, C1G, XC;
1965
1966    # find starting positions of iso classes of groups of size <= maxsize
1967    maxsize := CAT1_LIST_MAX_SIZE;
1968    iso := CAT1_LIST_CLASS_SIZES;
1969    count := 1;
1970    start := [ 1 ];
1971    for j in iso do
1972        count := count + j;
1973        Add( start, count );
1974    od;
1975    Info( InfoXMod, 2, "  iso = ", iso, "\n  start = ", start );
1976    if ( ( size < 1 ) or ( size > maxsize ) ) then
1977        Error( "only groups of order up to ", maxsize, " in CAT1_LIST");
1978        return false;
1979    fi;
1980    pos := start[ size ];
1981    if ( size < maxsize ) then
1982        pos2 := start[ size + 1 ] - 1;
1983    else
1984        pos2 := Length( CAT1_LIST );
1985    fi;
1986    names := List( [ pos..pos2], n -> CAT1_LIST[n][4] );
1987    j := pos + gpnum - 1;
1988    M := CAT1_LIST[j];
1989    G  := Group(M[4], ( ));
1990    SetName( G, M[3] );
1991    ncat1 := Length( M[5] ) + 1;
1992    genG := GeneratorsOfGroup( G );
1993    if not ( ( num >= 1 ) and ( num <= ncat1 ) ) then
1994        Print( "\nThere are ", ncat1, " cat1-structures for the group ");
1995        Print( G, ".\n" );
1996        Print( "[ [range gens], source & range names," );
1997        Print( " [tail genimages], [head genimages] ]" );
1998        Print( " :-\n" );
1999        Print( "[ ", genG, ",  tail = head = identity mapping ]\n" );
2000        for i in [2..ncat1] do
2001            Print( M[5][i-1], "\n" );
2002        od;
2003        Print( "Group has generators ", genG, "\n" );
2004        return ncat1;
2005    fi;
2006    if ( num = ncat1 ) then
2007        L := [ genG, genG, genG ];
2008    else
2009        L := M[5][num-1];
2010    fi;
2011    genR := L[1];
2012    R := Subgroup( G, genR );
2013    if ( G = R ) then
2014        SetName( R, Name(G) );
2015    fi;
2016    t := GroupHomomorphismByImages( G, R, genG, L[2] );
2017    h := GroupHomomorphismByImages( G, R, genG, L[3] );
2018    SetIsEndoMapping( t, true );
2019    SetIsEndoMapping( h, true );
2020    kert := Kernel( t );
2021    C1G := PreCat1GroupByEndomorphisms( t, h );
2022    ok := IsCat1Group( C1G );
2023    if ok then
2024        XC := XModOfCat1Group( C1G );
2025    fi;
2026    return C1G;
2027end );
2028
2029#############################################################################
2030##
2031#M  PreCat1GroupByTailHeadEmbedding
2032##
2033InstallMethod( PreCat1GroupByTailHeadEmbedding,
2034    "cat1-group from tail, head and embedding", true,
2035    [ IsGroupHomomorphism, IsGroupHomomorphism, IsGroupHomomorphism ], 0,
2036function( t, h, e )
2037
2038    local genG, R, genR, imh, imt, ime, eR, kert, kergen, bdy, imbdy,
2039          f, C1G, ok, G, PC;
2040
2041    G := Source( t );
2042    genG := GeneratorsOfGroup( G );
2043    R := Range( t );
2044    genR := SmallGeneratingSet( R );
2045    eR := Image( e );
2046    if not ( ( Source( h ) = G )
2047             and ( Image( h ) = R ) and ( Source( e ) = R )
2048             and IsInjective( e ) and IsSubgroup( G, eR ) )  then
2049        return fail;
2050    fi;
2051    imh := List( genG, x -> ImageElm( h, x ) );
2052    imt := List( genG, x -> ImageElm( t, x ) );
2053    ime := List( genR, x -> ImageElm( e, x ) );
2054    kert := Kernel( t );
2055    f := InclusionMappingGroups( G, kert );
2056    ## hres := GroupHomomorphismByImages( G, R, genG, imh );
2057    ## tres := GroupHomomorphismByImages( G, R, genG, imt );
2058    ## eres := GroupHomomorphismByImages( R, G, genR, ime );
2059    kergen := GeneratorsOfGroup( kert );
2060    imbdy := List( kergen, x -> ImageElm( h, x) );
2061    bdy := GroupHomomorphismByImages( kert, R, kergen, imbdy );
2062    PC := PreCat1Obj( t, h, e );
2063    SetBoundary( PC, bdy );
2064    SetKernelEmbedding( PC, f );
2065    return PC;
2066end );
2067
2068#############################################################################
2069##
2070#M  IsPreCat1GroupByEndomorphisms( <pcg> )
2071#M  PreCat1GroupByEndomorphisms( <et>, <eh> )
2072#M  EndomorphismPreCat1Group( <pcg> )
2073##
2074InstallMethod( IsPreCat1GroupByEndomorphisms, "tail & head are endomorphisms",
2075    true, [ IsPreCat1Group ], 0,
2076function( C1G )
2077    return IsSubgroup( Source( C1G ), Range( C1G ) );
2078end );
2079
2080InstallMethod( PreCat1GroupByEndomorphisms,
2081    "cat1-group from tail and head endomorphisms", true,
2082    [ IsGroupHomomorphism, IsGroupHomomorphism ], 0,
2083function( et, eh )
2084
2085    local G, gG, R, t, h, e;
2086
2087    if not ( IsEndoMapping( et ) and IsEndoMapping( eh ) ) then
2088        Print( "et, eh must both be group endomorphisms \n" );
2089		return fail;
2090    fi;
2091    if not ( Source( et ) = Source( eh ) ) then
2092        Info( InfoXMod, 2, "et and eh must have same source" );
2093	return fail;
2094    fi;
2095    G := Source( et );
2096    if not ( Image( et ) = Image( eh ) ) then
2097        Info( InfoXMod, 2, "et and eh must have same image" );
2098	return fail;
2099    fi;
2100    R := Image( et );
2101    gG := GeneratorsOfGroup( G );
2102    t := GroupHomomorphismByImages( G, R, gG, List( gG, g->ImageElm(et,g) ) );
2103    h := GroupHomomorphismByImages( G, R, gG, List( gG, g->ImageElm(eh,g) ) );
2104    e := InclusionMappingGroups( G, R );
2105    return PreCat1GroupByTailHeadEmbedding( t, h, e );
2106end );
2107
2108InstallMethod( EndomorphismPreCat1Group,
2109    "convert cat1-group to one with endomorphisms", true, [ IsPreCat1Group ], 0,
2110function( C1G )
2111
2112    local e, t, h;
2113
2114    if IsPreCat1GroupByEndomorphisms( C1G ) then
2115        return C1G;
2116    fi;
2117    e := RangeEmbedding( C1G );
2118    t := TailMap( C1G ) * e;
2119    h := HeadMap( C1G ) * e;
2120    return PreCat1GroupByEndomorphisms( t, h );
2121end );
2122
2123#############################################################################
2124##
2125#M  PreXModOfPreCat1Group
2126##
2127InstallMethod( PreXModOfPreCat1Group, true, [ IsPreCat1Group ], 0,
2128function( C1G )
2129
2130    local Csrc, Crng, gensrc, genrng, genker, bdy, kert, innaut, autgen,
2131           imautgen, idkert, a, aut, act, phi, j, r, PM, Cek, Cer, name;
2132
2133    Csrc := Source( C1G );
2134    Crng := Range( C1G );
2135    bdy := Boundary( C1G );
2136    Cer := RangeEmbedding( C1G );
2137    Cek := KernelEmbedding( C1G );
2138    kert := Kernel( C1G );
2139    if ( Size( kert ) = 1 ) then
2140        SetName( kert, "triv" );
2141    fi;
2142    gensrc := GeneratorsOfGroup( Csrc );
2143    genrng := GeneratorsOfGroup( Crng );
2144    genker := GeneratorsOfGroup( kert );
2145    if IsIdentityPreCat1Group( C1G ) then
2146        # X has trivial source and action
2147        aut := Group( IdentityMapping( kert ) );
2148        SetName( aut, "triv_aut" );
2149        act := MappingToOne( Crng, aut );
2150        SetName( act, "mapto1" );
2151    else
2152        autgen := [ ];
2153        for r in genrng do
2154            imautgen := List( genker, s -> ImageElm( Cek, s ) );
2155            imautgen := List( imautgen, g -> g^( ImageElm( Cer, r ) ) );
2156            imautgen := List( imautgen,
2157                              g -> PreImagesRepresentative( Cek, g ) );
2158            a := GroupHomomorphismByImages( kert, kert, genker, imautgen );
2159            Add( autgen, a );
2160        od;
2161        idkert := IdentityMapping( kert );
2162        aut := Group( autgen, idkert );
2163        act := GroupHomomorphismByImages( Crng, aut, genrng, autgen );
2164        if HasName( kert ) then
2165            SetName( aut, Concatenation( "innaut(", Name( kert ), ")" ) );
2166        else
2167            SetName( aut, "aut" );
2168        fi;
2169        if not IsGroupHomomorphism( act ) then
2170            Error( "act is not a homomorphism" );
2171        fi;
2172    fi;
2173    PM := PreXModObj( bdy, act );
2174    #?  aded 30/04/08 - but is it really needed ??
2175    if ( IsSubgroup( Crng, kert ) and IsNormal( Crng, kert ) ) then
2176        SetIsNormalSubgroup2DimensionalGroup( PM, true );
2177    fi;
2178    if ( HasName( Source( bdy ) ) and HasName( Range( bdy ) ) ) then
2179        name := Name( PM );
2180    elif HasName( C1G ) then
2181        SetName( PM, Concatenation( "xmod(", Name( C1G ), ")" ) );
2182    fi;
2183    SetPreCat1GroupOfPreXMod( PM, rec(
2184        precat1 := C1G,
2185        xmodSourceEmbedding := kert,
2186        xmodSourceEmbedddingIsomorphism := Cek,
2187        xmodRangeEmbedding := Image( Cer ),
2188        xmodRangeEmbeddingIsomorphism := Cer ) );
2189    return PM;
2190end );
2191
2192#############################################################################
2193##
2194#M  Source( C1G ) . . . . . . . . . . . . . . . . . . . .  for a cat1-group
2195##
2196InstallOtherMethod( Source,
2197    "method for a pre-cat1-group",
2198    true,
2199    [ IsPreCat1Group ], 0,
2200    C1G -> Source( TailMap( C1G ) ) );
2201
2202##############################################################################
2203##
2204#M  Range( C1G ) . . . . . . . . . . . . . . . . . . . . . for a cat1-group
2205##
2206InstallOtherMethod( Range,
2207    "method for a pre-cat1-group",
2208    true,
2209    [ IsPreCat1Group ], 0,
2210    C1G -> Range( TailMap( C1G ) ) );
2211
2212##############################################################################
2213##
2214#M  Kernel( C1G ) . . . . . . . . . . . . . . . . . . . for a pre-cat1-group
2215##
2216InstallOtherMethod( Kernel,
2217    "method for a pre-cat1-group", true, [ IsPreCat1Group ], 0,
2218    C1G -> Kernel( TailMap( C1G ) ) );
2219
2220#############################################################################
2221##
2222#M  Boundary( C1G ) . . . . . . . . . . . . . . . . . . .  for a cat1-group
2223##
2224InstallOtherMethod( Boundary,
2225    "method for a pre-cat1-group", true, [ IsPreCat1Group ], 0,
2226    C1G -> GeneralRestrictedMapping( HeadMap(C1G), Kernel(C1G), Range(C1G) ) );
2227
2228#############################################################################
2229##
2230#M  KernelEmbedding( C1G ) . . .  . . . . . . . . . . . . .  for a cat1-group
2231##
2232InstallMethod( KernelEmbedding,
2233    "method for a pre-cat1-group", true, [ IsPreCat1Group ], 0,
2234    C1G -> InclusionMappingGroups( Source( C1G ), Kernel( C1G ) ) );
2235
2236##############################################################################
2237##
2238#M  Cat1GroupByPeifferQuotient . . . . cat1 from pre-cat1 and Peiffer subgroup
2239##
2240InstallMethod( Cat1GroupByPeifferQuotient,
2241               "cat1-group from a pre-cat1-group and Peiffer subgroup",
2242               true, [ IsPreCat1Group ], 0,
2243function( PC )
2244
2245    local PCrng, PCsrc, PCt, PCh, PCe, genrng, gensrc, Pf, nat, quot,
2246           qgen, pqgen, tpqgen, hpqgen, tail, head, ime, embed, C1G;
2247
2248    PCrng := Range( PC ) ;
2249    genrng := GeneratorsOfGroup( PCrng );
2250    PCsrc := Source( PC );
2251    gensrc := GeneratorsOfGroup( PCsrc );
2252    PCt := TailMap( PC );
2253    PCh := HeadMap( PC );
2254    PCe := RangeEmbedding( PC );
2255    # construct the quotient
2256    Pf := PeifferSubgroupPreCat1Group( PC );
2257    if not IsNormal( PCsrc, Pf ) then
2258        Error( "Peiffer subgroup not normal in source group" );
2259    fi;
2260    nat := NaturalHomomorphismByNormalSubgroup( PCsrc, Pf );
2261    quot := Image( nat );
2262    qgen := GeneratorsOfGroup( quot );
2263    # construct the head, tail and embedding
2264    pqgen := List( qgen, q -> PreImagesRepresentative( nat, q ) );
2265    tpqgen := List( pqgen, p -> ImageElm( PCt, p ) );
2266    tail := GroupHomomorphismByImages( quot, PCrng, qgen, tpqgen );
2267    hpqgen := List( pqgen, p -> ImageElm( PCh, p ) );
2268    head := GroupHomomorphismByImages( quot, PCrng, qgen, hpqgen );
2269    ime := List( genrng, r -> ImageElm( nat, ImageElm( PCe, r ) ) );
2270    embed := GroupHomomorphismByImages( PCrng, quot, genrng, ime );
2271    C1G := PreCat1GroupByTailHeadEmbedding( tail, head, embed );
2272    if not IsCat1Group( C1G ) then
2273        Error( "fails to be a cat1-group" );
2274    fi;
2275    return C1G;
2276end );
2277
2278##############################################################################
2279##
2280#M  DiagonalCat1Group . . . . . . cat1-group of the form (GxG => G) with t<>h
2281##
2282InstallMethod( DiagonalCat1Group, "cat1-group from a list of generators",
2283    true, [ IsList ], 0,
2284function( gen1 )
2285
2286    local m, lgen, gen2, genR, i, p, L1, len1, L2, j,
2287           G, R, genG, one, ids, t, h, e, C;
2288
2289    m := Maximum( List( gen1, g -> LargestMovedPoint(g) ) );
2290    lgen := Length( gen1 );
2291    gen2 := ShallowCopy( gen1 );
2292    genR := ShallowCopy( gen1 );
2293    for i in [1..lgen] do
2294        p := gen1[i];
2295        L1 := ListPerm( p );
2296        len1 := Length( L1 );
2297        L2 := [1..2*m];
2298        for j in [1..len1] do
2299            L2[m+j] := L1[j]+m;
2300        od;
2301        gen2[i] := PermList( L2 );
2302        for j in [1..len1] do
2303            L2[j] := L1[j];
2304        od;
2305        genR[i] := PermList( L2 );
2306    od;
2307    genG := Concatenation( gen1, gen2 );
2308    G := Group( genG );
2309    R := Group( genR );
2310    one := One( G );
2311    ids := ListWithIdenticalEntries( lgen, one );
2312    t := GroupHomomorphismByImages( G, R, genG, Concatenation( genR, ids ) );
2313    h := GroupHomomorphismByImages( G, R, genG, Concatenation( ids, genR ) );
2314    e := GroupHomomorphismByImages( R, G, genR, genR );
2315    C := PreCat1GroupByTailHeadEmbedding( t, h, e );
2316    return C;
2317end );
2318
2319##############################################################################
2320##
2321#M  AllCat1GroupsWithImage  . . . . . . cat1-group structures with given range
2322#O  AllCat1GroupsWithImageIterator( <gp> )  . . iterator for the previous list
2323#F  NextIterator_AllCat1GroupsWithImage( <iter> )
2324#F  IsDoneIterator_AllCat1GroupsWithImage( <iter> )
2325#F  ShallowCopy_AllCat1GroupsWithImage( <iter> )
2326#M  AllCat1GroupsWithImageUpToIsomorphism  . . . . . iso class reps for G => R
2327##
2328BindGlobal( "NextIterator_AllCat1GroupsWithImage", function ( iter )
2329
2330    local C, post, pair, t, posh, h, ok;
2331
2332    ok := false;
2333    while ( not ok ) and ( not IsDoneIterator( iter!.pairsIterator ) ) do
2334        pair := NextIterator( iter!.pairsIterator );
2335        ## could attempt to be clever and not calculate t every time
2336        t := GroupHomomorphismByImages( iter!.group, iter!.group,
2337                                        iter!.gens, iter!.images[ pair[1] ] );
2338        h := GroupHomomorphismByImages( iter!.group, iter!.group,
2339                                        iter!.gens, iter!.images[ pair[2] ] );
2340        C := PreCat1GroupByEndomorphisms( t, h );
2341        if ( not ( C = fail ) and IsCat1Group( C ) ) then
2342            ok := true;
2343            return C;
2344        fi;
2345        if IsDoneIterator( iter!.pairsIterator ) then
2346            return fail;
2347        fi;
2348    od;
2349end );
2350
2351BindGlobal( "IsDoneIterator_AllCat1GroupsWithImage",
2352    iter -> IsDoneIterator( iter!.pairsIterator )
2353);
2354
2355BindGlobal( "ShallowCopy_AllCat1GroupsWithImage",
2356    iter -> rec( group := iter!.group,
2357                  gens := iter!.gens,
2358                images := iter!.images,
2359         pairsIterator := ShallowCopy( iter!.pairsIterator )
2360    )
2361);
2362
2363InstallGlobalFunction( "DoAllCat1GroupsWithImageIterator",
2364function( G, R )
2365
2366    local data, genG, images, found, len, i, lenIterator, pairsIterator, iter;
2367
2368    data := IdempotentEndomorphismsData( G );
2369    genG := data.gens;
2370    images := data.images;
2371    found := false;
2372    len := Length( images );
2373    i := 0;
2374    while ( not found ) and ( i < len ) do
2375        i := i+1;
2376        if ( R = Subgroup( G, images[i][1] ) ) then
2377            found := true;
2378            images := images[i];
2379        fi;
2380    od;
2381    if not found then
2382        ## there are no idempotent endomorphisms with image R
2383        return IteratorList( [ ] );
2384    fi;
2385    lenIterator := IteratorList( [1..Length(images)] );
2386    pairsIterator := CartesianIterator( lenIterator, lenIterator );
2387    iter := IteratorByFunctions(
2388        rec(     group := G,
2389                  gens := genG,
2390                images := images,
2391         pairsIterator := ShallowCopy( pairsIterator ),
2392          NextIterator := NextIterator_AllCat1GroupsWithImage,
2393        IsDoneIterator := IsDoneIterator_AllCat1GroupsWithImage,
2394           ShallowCopy := ShallowCopy_AllCat1GroupsWithImage ) );
2395    return iter;
2396end );
2397
2398InstallMethod( AllCat1GroupsWithImageIterator, "for a group and a subgroup",
2399    [ IsGroup, IsGroup ], 0,
2400function( G, R )
2401    if not IsSubgroup( G, R ) then
2402        Error( "R is not a subgroup of G" );
2403    fi;
2404    return DoAllCat1GroupsWithImageIterator( G, R );
2405end );
2406
2407InstallMethod( AllCat1GroupsWithImage, "for a group and a subgroup",
2408    [ IsGroup, IsGroup ], 0,
2409function( G, R )
2410
2411    local L, C;
2412
2413    L := [ ];
2414    for C in AllCat1GroupsWithImageIterator( G, R ) do
2415        if not ( C = fail ) then
2416           Add( L, C );
2417        fi;
2418    od;
2419    return L;
2420end );
2421
2422InstallMethod( AllCat1GroupsWithImageNumber, "for a group and a subgroup",
2423    [ IsGroup, IsGroup ], 0,
2424function( G, R )
2425
2426    local n, C;
2427
2428    n := 0;
2429    for C in AllCat1GroupsWithImageIterator( G, R ) do
2430        if not ( C = fail ) then
2431            n := n+1;
2432        fi;
2433    od;
2434    return n;
2435end );
2436
2437##############################################################################
2438##
2439#M  AllCat1Groups . . . . . . . list of cat1-group structures on a given group
2440#O  AllCat1GroupsIterator( <gp> ) . . . . . . . iterator for the previous list
2441#F  NextIterator_AllCat1Groups( <iter> )
2442#F  IsDoneIterator_AllCat1Groups( <iter> )
2443#F  ShallowCopy_AllCat1Groups( <iter> )
2444#A  AllCat1GroupsNumber( <gp> ) . . . . . . . . .  number of these cat1-groups
2445#M  AllCat1GroupsUpToIsomorphism . . . iso class reps of cat1-group structures
2446##
2447BindGlobal( "NextIterator_AllCat1Groups", function ( iter )
2448    local R, C;
2449    if IsDoneIterator( iter!.imagesIterator ) then
2450        R := NextIterator( iter!.subsIterator );
2451        iter!.imagesIterator :=
2452            AllCat1GroupsWithImageIterator( iter!.group, R );
2453        ## but this iterator might be empty, so:
2454        if IsDoneIterator( iter!.imagesIterator ) then
2455            return fail;
2456        fi;
2457    fi;
2458    return NextIterator( iter!.imagesIterator );
2459end );
2460
2461BindGlobal( "IsDoneIterator_AllCat1Groups",
2462    iter -> ( IsDoneIterator( iter!.subsIterator )
2463              and IsDoneIterator( iter!.imagesIterator ) )
2464);
2465
2466BindGlobal( "ShallowCopy_AllCat1Groups",
2467    iter -> rec( group := iter!.group,
2468          subsIterator := ShallowCopy( iter!.subsIterator ),
2469        imagesIterator := ShallowCopy( iter!.imagesIterator )
2470    )
2471);
2472
2473InstallGlobalFunction( "DoAllCat1GroupsIterator",
2474function( G )
2475
2476    local subsIterator, imagesIterator, iter;
2477
2478    subsIterator := AllSubgroupsIterator( G );
2479    imagesIterator := IteratorList( [ ] );
2480    iter := IteratorByFunctions(
2481        rec(     group := G,
2482          subsIterator := ShallowCopy( subsIterator ),
2483        imagesIterator := ShallowCopy( imagesIterator ),
2484          NextIterator := NextIterator_AllCat1Groups,
2485        IsDoneIterator := IsDoneIterator_AllCat1Groups,
2486           ShallowCopy := ShallowCopy_AllCat1Groups ) );
2487    return iter;
2488end );
2489
2490InstallMethod( AllCat1GroupsIterator, "for a group", [ IsGroup ], 0,
2491    G -> DoAllCat1GroupsIterator( G ) );
2492
2493InstallMethod( AllCat1Groups, "for a group", [ IsGroup ], 0,
2494function( G )
2495
2496    local L, C, images, lens;
2497
2498    InitCatnGroupRecords( G );
2499    L := [ ];
2500    for C in AllCat1GroupsIterator( G ) do
2501       if not ( C = fail ) then
2502           Add( L, C );
2503        fi;
2504    od;
2505    if not IsBound( CatnGroupNumbers( G ).idem ) then
2506        images := IdempotentEndomorphismsData( G ).images;
2507        lens := List( images, L -> Length( L ) );
2508        CatnGroupNumbers( G ).idem := Sum( lens );
2509    fi;
2510    if not IsBound( CatnGroupNumbers( G ).cat1 ) then
2511        CatnGroupNumbers( G ).cat1 := Length( L );
2512    fi;
2513    return L;
2514end );
2515
2516InstallMethod( AllCat1GroupsNumber, "for a group", [ IsGroup ], 0,
2517function( G )
2518
2519    local n, C, all;
2520
2521    InitCatnGroupRecords( G );
2522    if IsBound( CatnGroupNumbers( G ).cat1 ) then
2523        return CatnGroupNumbers( G ).cat1;
2524    fi;
2525    ## not already known, so perform the calculation
2526    all := AllCat1Groups( G );
2527    return CatnGroupNumbers( G ).cat1;
2528end );
2529
2530InstallMethod( AllCat1GroupsUpToIsomorphism, "iso class reps of cat1-groups",
2531    true, [ IsGroup ], 0,
2532function( G )
2533
2534    local L, numL, i, k, C, ok, found, iso, images, lens;
2535
2536    InitCatnGroupRecords( G );
2537    L := [ ];
2538    i := 0;
2539    numL := 0;
2540    for C in AllCat1GroupsIterator( G ) do
2541        if not ( C = fail ) then
2542            i := i+1;
2543            k := 0;
2544            found := false;
2545            while ( not found ) and ( k < numL ) do
2546                k := k+1;
2547                iso := IsomorphismCat1Groups( C, L[k] );
2548                if ( iso <> fail ) then
2549                     found := true;
2550                fi;
2551            od;
2552            if not found then
2553                Add( L, C );
2554                numL := numL + 1;
2555            fi;
2556        fi;
2557    od;
2558    if not IsBound( CatnGroupNumbers( G ).idem ) then
2559        images := IdempotentEndomorphismsData( G ).images;
2560        lens := List( images, L -> Length( L ) );
2561        CatnGroupNumbers( G ).idem := Sum( lens );
2562    fi;
2563    if not IsBound( CatnGroupNumbers( G ).cat1 ) then
2564        CatnGroupNumbers( G ).cat1 := i;
2565    fi;
2566    if not IsBound( CatnGroupNumbers( G ).iso1 ) then
2567        CatnGroupNumbers( G ).iso1 := numL;
2568    fi;
2569    return L;
2570end );
2571
2572#############################################################################
2573##
2574#M  DirectProductInfo( <obj> ) . . . . . . . . . . . . . . . . for 2d-objects
2575#M  Coproduct2dInfo( <obj> ) . . . . . . . . . . . . . . . . . for 2d-objects
2576#M  DirectProductOp(  )  . . . . . .  (bdy1 x bdy2) : (S1 x S2) --> (R1 x R2)
2577##
2578InstallOtherMethod( DirectProductInfo, "generic method for 2d-objects", true,
2579    [ Is2DimensionalDomain ], 0,
2580function( obj )
2581    return rec( objects := [ ],
2582                embeddings := [ ],
2583                projections := [ ] );
2584end );
2585
2586InstallMethod( Coproduct2dInfo, "generic method for 2d-objects", true,
2587    [ Is2DimensionalDomain ], 0,
2588function( obj )
2589    return rec( objects := [ ],
2590                embeddings := [ ],
2591                projections := [ ] );
2592end );
2593
2594#?  (19/07/07) : allowed for case when one of Xsrc,Xrng,Ysrc,Yrng trivial ##
2595#?               using parameter list: spec(=[0,0,0,0] by default)        ##
2596
2597InstallOtherMethod( DirectProductOp,
2598    "method for pre-crossed modules", true, [ IsList, IsPreXMod ], 0,
2599function( list, X1 )
2600
2601    local Xsrc, Xrng, Y1, Ysrc, Yrng, genXrng, genYrng, genXsrc, genYsrc,
2602           XSpos, YSpos, XRpos, YRpos, Spos, imaut, autgen, aut, act,
2603           XY, S, R, genS, lenS, genR, lenR, imbdy, bdy, a, i, j, k,
2604           Xbdy, Ybdy, Xact, Yact, imXbdy, imYbdy, alpha, info,
2605           eXS, eYS, pXS, pYS, eXR, eYR, spec;
2606
2607    if not ( Length( list ) = 2 ) then
2608        Error( "direct product not yet implemented for more than 2 terms" );
2609    fi;
2610    if not ( list[1] = X1 ) then
2611        Error( "second argument should be first entry in first argument list" );
2612    fi;
2613    Y1 := list[2];
2614    ##  first the source group
2615    Xsrc := Source( X1 );
2616    Ysrc := Source( Y1 );
2617    genXsrc := GeneratorsOfGroup( Xsrc );
2618    genYsrc := GeneratorsOfGroup( Ysrc );
2619    spec := [false,false,false,false];
2620    if ( Size( Xsrc ) = 1 ) then
2621        spec[1] := true;
2622    elif ( Size( Ysrc ) = 1 ) then
2623        spec[3] := true;
2624    fi;
2625    if spec[1] then
2626        S := Ysrc;
2627        eYS := IdentityMapping( S );
2628        pYS := IdentityMapping( S );
2629    elif spec[3] then
2630        S := Xsrc;
2631        eXS := IdentityMapping( S );
2632        pXS := IdentityMapping( S );
2633    else
2634        S := DirectProduct( Xsrc, Ysrc );
2635        if ( not HasName( S ) and HasName( Xsrc ) and HasName( Ysrc ) ) then
2636            SetName( S, Concatenation( Name( Xsrc ), "x", Name( Ysrc ) ) );
2637        fi;
2638        eXS := Embedding( S, 1 );
2639        eYS := Embedding( S, 2 );
2640        pXS := Projection( S, 1 );
2641        pYS := Projection( S, 2 );
2642    fi;
2643    genS := GeneratorsOfGroup( S );
2644    lenS := Length( genS );
2645    Spos := [ 1..lenS ];
2646    if spec[1] then
2647        XSpos := [ ];
2648        YSpos := [ 1..Length( genYsrc ) ];
2649    elif spec[3] then
2650        XSpos := [ 1..Length( genXsrc ) ];
2651        YSpos := [ ];
2652    else
2653        XSpos := [ 1..Length( genXsrc ) ];
2654        YSpos := [ 1+Length( genXsrc ) .. lenS ];
2655    fi;
2656    ##  now for the range group
2657    Xrng := Range( X1 );
2658    Yrng := Range( Y1 );
2659    genXrng := GeneratorsOfGroup( Xrng );
2660    genYrng := GeneratorsOfGroup( Yrng );
2661    if ( Size( Xrng ) = 1 ) then
2662        spec[2] := true;
2663    elif ( Size( Yrng ) = 1 ) then
2664        spec[4] := true;
2665    fi;
2666    if spec[2] then
2667        R := Yrng;
2668        eXR := MappingToOne( Xrng, Yrng );
2669        eYR := IdentityMapping( R );
2670    elif spec[4] then
2671        R := Xrng;
2672        eXR := IdentityMapping( R );
2673        eYR := MappingToOne( Yrng, Xrng );
2674    else
2675        R := DirectProduct( Xrng, Yrng );
2676        if ( not HasName( R ) and HasName( Xrng ) and HasName( Yrng ) ) then
2677            SetName( R, Concatenation( Name( Xrng ), "x", Name( Yrng ) ) );
2678        fi;
2679        eXR := Embedding( R, 1 );
2680        eYR := Embedding( R, 2 );
2681    fi;
2682    genR := GeneratorsOfGroup( R );
2683    lenR := Length( genR );
2684    if spec[2] then
2685        XRpos := [ ];
2686        YRpos := [ 1..Length( genYrng ) ];
2687    elif spec[4] then
2688        XRpos := [ 1..Length( genXrng ) ];
2689        YRpos := [ ];
2690    else
2691        XRpos := [ 1..Length( genXrng ) ];
2692        YRpos := [ 1+Length( genXrng ) .. lenR ];
2693    fi;
2694    ##  now for the boundary
2695    Xbdy := Boundary( X1 );
2696    Ybdy := Boundary( Y1 );
2697    Xact := XModAction( X1 );
2698    Yact := XModAction( Y1 );
2699    imXbdy := List( genS{ XSpos },
2700        s -> ImageElm( eXR, ImageElm( Xbdy, ImageElm( pXS, s ) ) ) );
2701    imYbdy := List( genS{ YSpos },
2702        s -> ImageElm( eYR, ImageElm( Ybdy, ImageElm( pYS, s ) ) ) );
2703    imbdy := Concatenation( imXbdy, imYbdy );
2704    bdy := GroupHomomorphismByImages( S, R, genS, imbdy );
2705    autgen := 0 * [ 1..lenR ];
2706    for i in XRpos do
2707        a := ImageElm( Xact, genXrng[i] );
2708        imaut := 0 * Spos;
2709        for j in YSpos do
2710            imaut[j] := genS[j];
2711        od;
2712        for j in XSpos do
2713            imaut[j] := ImageElm( eXS, ImageElm( a, ImageElm(pXS,genS[j] ) ) );
2714        od;
2715        alpha := GroupHomomorphismByImages( S, S, genS, imaut );
2716        autgen[i] := alpha;
2717    od;
2718    if spec[2] then
2719        k := 0;
2720    else
2721        k := Length( genXrng );
2722    fi;
2723    for i in YRpos do
2724        a := ImageElm( Yact, genYrng[i-k] );
2725        imaut := 0 * Spos;
2726        for j in XSpos do
2727            imaut[j] := genS[j];
2728        od;
2729        for j in YSpos do
2730            imaut[j] := ImageElm( eYS, ImageElm( a, ImageElm(pYS,genS[j] ) ) );
2731        od;
2732        alpha := GroupHomomorphismByImages( S, S, genS, imaut );
2733        autgen[i] := alpha;
2734    od;
2735    aut := Group( autgen );
2736    act := GroupHomomorphismByImages( R, aut, genR, autgen );
2737    XY := PreXModByBoundaryAndAction( bdy, act );
2738    if ( IsXMod( X1 ) and IsXMod( Y1 ) ) then
2739        SetIsXMod( XY, true );
2740    fi;
2741    if ( HasName( X1 ) and HasName( Y1 ) ) then
2742        SetName( XY, Concatenation( Name( X1 ), "x", Name( Y1 ) ) );
2743    elif ( HasName( Source(XY) ) and HasName( Range(XY) ) ) then
2744        SetName( XY, Concatenation( "[", Name( Source(XY ) ),
2745                     "->", Name( Range(XY) ), "]" ) );
2746    fi;
2747    info := DirectProductInfo( XY );
2748    info!.objects := [ X1, Y1 ];
2749    return XY;
2750end );
2751
2752##############################################################################
2753##
2754#M  Embedding . . . . for direct products of (pre-)xmods and (pre-)cat1-groups
2755##
2756InstallOtherMethod( Embedding, "generic method for (pre-)xmods & (pre-)cat1s",
2757    true, [ Is2DimensionalGroup, IsPosInt ], 0,
2758function( D, i )
2759    local info, eS, eR, obj, mor;
2760
2761    info := DirectProductInfo( D );
2762    if IsBound( info!.embeddings[i] ) then
2763        return info!.embeddings[i];
2764    fi;
2765    eS := Embedding( Source( D ), i );
2766    eR := Embedding( Range( D ), i );
2767    Info( InfoXMod, 3, "SourceEmbedding: ", eS );
2768    Info( InfoXMod, 3, " RangeEmbedding: ", eR );
2769    obj := info!.objects[i];
2770    if IsPreXMod( D ) then
2771        mor := PreXModMorphism( obj, D, eS, eR );
2772    elif IsPreCat1Group( D ) then
2773        mor := PreCat1GroupMorphism( obj, D, eS, eR );
2774    else
2775        mor := fail;
2776    fi;
2777    if not ( mor = fail ) then
2778        SetIsInjective( mor, true );
2779        info!.embeddings[i] := mor;
2780    fi;
2781    return mor;
2782end );
2783
2784##############################################################################
2785##
2786#M  Projection . . .  for direct products of (pre-)xmods and (pre-)cat1-groups
2787##
2788InstallOtherMethod( Projection, "generic method for (pre-)xmods & (pre-)cat1s",
2789    true, [ Is2DimensionalGroup, IsPosInt ], 0,
2790function( D, i )
2791    local G, info, pS, pR, mor;
2792
2793    G := Source( D );
2794    if HasDirectProductInfo( G ) then
2795        info := DirectProductInfo( D );
2796        if not ( i in [1,2] ) then
2797            Error( "only two projections available" );
2798        fi;
2799    else
2800        info := SemidirectProductInfo( G );
2801        if not ( i = 1 ) then
2802            Error( "only the first projection is available" );
2803        fi;
2804    fi;
2805    if IsBound( info!.projections[i] ) then
2806        return info!.projections[i];
2807    fi;
2808    pS := Projection( G, i );
2809    pR := Projection( Range( D ), i );
2810    if IsPreXMod( D ) then
2811        mor := PreXModMorphism( info!.objects[i], D, pS, pR );
2812    elif IsPreCat1Group( D ) then
2813        mor := PreCat1GroupMorphism( info!.objects[i], D, pS, pR );
2814    else
2815        mor := fail;
2816    fi;
2817    if not ( mor = fail ) then
2818        SetIsInjective( mor, true );
2819        info!.projections[i] := mor;
2820    fi;
2821    return mor;
2822end );
2823
2824##############################################################################
2825##
2826#M  TrivialSub2DimensionalGroup . . . . . . . . . .  of a 2d-object
2827#M  TrivialSubPreXMod  . . . . . . . . . . . . . . . of a pre-crossed module
2828#M  TrivialSubXMod     . . . . . . . . . . . . . . . of a crossed module
2829#M  TrivialSubPreCat1Group . . . . . . . . . . . . . of a pre-cat1-group
2830#M  TrivialSubCat1Group  . . . . . . . . . . . . . . of a cat1-group
2831##
2832InstallMethod( TrivialSub2DimensionalGroup, "of a 2d-object", true,
2833    [ Is2DimensionalGroup ], 0,
2834function( obj )
2835
2836    local idsrc, idrng;
2837
2838    idsrc := TrivialSubgroup( Source( obj ) );
2839    idrng := TrivialSubgroup( Range( obj ) );
2840    if IsPreXMod( obj ) then
2841        return SubPreXMod( obj, idsrc, idrng );
2842    elif IsPreCat1Group( obj ) then
2843        return SubPreCat1Group( obj, idsrc );
2844    else
2845        Error( "<obj> must be a pre-crossed module or a pre-cat1-group" );
2846    fi;
2847end );
2848
2849InstallMethod( TrivialSubPreXMod, "of a pre-crossed module", true,
2850    [ IsPreXMod ], 0,
2851function( obj )
2852    return TrivialSub2DimensionalGroup( obj );
2853end );
2854
2855InstallMethod( TrivialSubXMod, "of a crossed module", true, [ IsXMod ], 0,
2856function( obj )
2857    return TrivialSub2DimensionalGroup( obj );
2858end );
2859
2860InstallMethod( TrivialSubPreCat1Group, "of a pre-cat1-group", true,
2861    [ IsPreCat1Group ], 0,
2862function( obj )
2863    return TrivialSub2DimensionalGroup( obj );
2864end );
2865
2866InstallMethod( TrivialSubCat1Group, "of a cat1-group", true, [ IsCat1Group ], 0,
2867function( obj )
2868    return TrivialSub2DimensionalGroup( obj );
2869end );
2870
2871##############################################################################
2872##
2873#M  IsNormalSubgroup2DimensionalGroup . . . . . . . . for 2Dimensional-objects
2874##
2875InstallMethod( IsNormalSubgroup2DimensionalGroup,
2876    "for crossed modules and cat1-groups", [ Is2DimensionalGroup ], 0,
2877function( obj )
2878    local src, rng, gensrc, genrng;
2879    src := Source( obj );
2880    rng := Range( obj );
2881    gensrc := GeneratorsOfGroup( src );
2882    if IsXMod( obj ) then
2883        return ( IsNormal(rng,src) and
2884                 ( gensrc = List( gensrc, s -> ImageElm( Boundary(obj), s ) ) ) );
2885    elif IsCat1Group( obj ) then
2886        return IsNormalSubgroup2DimensionalGroup( XModOfCat1Group( obj ) );
2887    else
2888        Error( "method not yet implemented" );
2889    fi;
2890end );
2891
2892##############################################################################
2893##
2894#M  IsNormal . . . . . . . . . . . . . . . . . . . .  for 2Dimensional-objects
2895##
2896InstallOtherMethod( IsNormal, "for precrossed modules", IsIdenticalObj,
2897    [ IsPreXMod, IsPreXMod ], 0,
2898function( XM, SM )
2899
2900    local xr, a, ss, im, xs, sr, Ssrc, Xact, snat, rnat;
2901
2902    if not IsSubPreXMod( XM, SM ) then
2903        return false;
2904    fi;
2905    Ssrc := Source( SM );
2906    Xact := XModAction( XM );
2907    for xr in GeneratorsOfGroup( Range( XM ) ) do
2908        a := ImageElm( Xact, xr );
2909        for ss in GeneratorsOfGroup( Ssrc ) do
2910            im := ImageElm( a, ss );
2911            if not ( im in Ssrc ) then
2912                Info( InfoXMod, 2, "ss,xr,ss^xr = ", [ss,xr,im] );
2913                return false;
2914            fi;
2915        od;
2916    od;
2917    for sr in GeneratorsOfGroup( Range( SM ) ) do
2918        a := ImageElm( Xact, sr );
2919        for xs in GeneratorsOfGroup( Source( XM ) ) do
2920            im := xs^(-1) * ImageElm( a, xs );
2921            if not ( im in Ssrc ) then
2922                Info( InfoXMod, 3, "sr,xs,sr^(-1)*xs^sr = ", [sr,xs,im] );
2923                return false;
2924            fi;
2925        od;
2926    od;
2927    return true;
2928end );
2929
2930##############################################################################
2931##
2932#M  NormalSubXMods  .  . . . . . . . . . . . . . . . . .  for a crossed module
2933##
2934InstallMethod( NormalSubXMods, "for a crossed module", true, [ IsXMod ], 0,
2935function( XM )
2936
2937    local Xsrc, Xrng, YM, i, j, slen, rlen, norm, normsrc, normrng, ok;
2938
2939    Xsrc := Source( XM );
2940    Xrng := Range( XM );
2941    norm := [ ];
2942    normsrc := NormalSubgroups( Xsrc );
2943    normrng := NormalSubgroups( Xrng );
2944    slen := Length( normsrc );
2945    rlen := Length( normrng );
2946    for i in [ 1..slen ] do
2947        for j in [ 1..rlen ] do
2948            if ( ( i = 1 ) and ( j = 1 ) ) then
2949                YM := TrivialSubXMod( XM );
2950            elif ( ( i = slen ) and ( j = rlen ) ) then
2951                YM := XM;
2952            else
2953                YM := SubXMod( XM, normsrc[i], normrng[j] );
2954            fi;
2955            ok := not ( YM = fail );
2956            if ( ok and IsXMod( YM ) and IsNormal( XM, YM ) ) then
2957                Add( norm, YM );
2958            fi;
2959        od;
2960    od;
2961    return norm;
2962end );
2963
2964##############################################################################
2965##
2966#M  InitCatnGroupRecords . . . . . . . . . . . . . . . . . . . . . for a group
2967##
2968InstallMethod( InitCatnGroupRecords, "for a group", true, [ IsGroup ], 0,
2969function( G )
2970
2971    if not HasCatnGroupNumbers( G ) then
2972        SetCatnGroupNumbers( G, rec() );
2973    fi;
2974    if not HasCatnGroupNumbers( G ) then
2975        Error( "CatnGroupNumbers not set" );
2976    fi;
2977    if not HasCatnGroupLists( G ) then
2978        SetCatnGroupLists( G, rec() );
2979        CatnGroupLists( G ).omit := false;
2980    fi;
2981    if not HasCatnGroupLists( G ) then
2982        Error( "CatnGroupLists not set" );
2983    fi;
2984end );
2985